n8nとNotion連携:データベースに書き込む・読み出す

公開 書いた人 大林 諒

「問い合わせ受付」ワークフローの記録先にNotionのデータベースを足し、担当と対応状況をNotionで管理できるようにします。読み終わると、問い合わせがNotionに1件ずつ入り、未対応の件数が毎朝Slackに届き、Notionで「完了」にしたら #問い合わせ に知らせが来る状態になります。前提は「n8nの使い方入門:最初のワークフローを作って動かす」で作った3つのノードのワークフローがあることと、Notionのワークスペースのオーナー権限を持つ人が社内にいることです。版は2026年10月時点の安定版2.44.7で書いています。

この記事の内容
  1. 先に決めること
  2. 作るものの一覧
  3. 手順
  4. 応用
  5. できないことと、その代わり
  6. よく詰まるところ
  7. 次にやること

先に決めること

スプレッドシートと何を分けるか

Notionが得意なのは、1件ずつ開いて担当を決め、メモを書き、状態を動かすことです。件数を数える・月ごとに並べるといった集計は、スプレッドシートのほうが向いています。実際に運用していると、記録はGoogleスプレッドシート「問い合わせ一覧」に残したまま、対応の管理だけをNotionに移す形が一番揉めません。この記事もその形で、フォームから届いた1件をシートとNotionの両方に書きます。シート側の書き方は「n8nとGoogleスプレッドシート連携:読み書き・更新・重複を防ぐ」にあります。

認証は内部コネクションにする

n8nのNotion用Credentialには「Notion API」(APIキー)と「Notion OAuth2 API」の2種類があります。自社のワークスペースだけで使うなら、Notionの内部コネクション(internal connection)を作り、そのAPIトークンを「Notion API」に貼るのが早いです。内部コネクションは特定の人ではなく、それ自体がボットのユーザーとして動きます。共有した社員が退職しても、ページへのアクセスはコネクションに残ります。OAuth2は他社のワークスペースにも配るアプリ(公開コネクション)を作るときの方法で、自社の自動化には要りません。

内部コネクションを作れるのはワークスペースのオーナーだけです。オーナーが情シスや代表なら、その人に作ってもらいます。

共有するページを決める

コネクションは、作っただけでは何も読めません。ページかデータベースを明示的に共有して、初めて読み書きできるようになります。親のページを共有すると子のページにも届くので、「業務自動化」のような親ページを1つ作り、その下にn8nが触るデータベースを集めておくと管理が楽です。ワークスペース全体を見せる必要はありません。

プロパティの型と選択肢を先に作る

「ステータス」型の選択肢は、ページを作る・更新する操作では増えません。n8nのNotionノードがやるのはこの操作なので、「未対応」「対応中」「完了」はNotionの画面で先に作っておきます。逆に「セレクト」型は、無い名前を送ると選択肢が自動で増えます(コネクションに書き込み権限があるとき)。表記ゆれがそのまま選択肢になるので、n8n側で値を揃えてから送ります。

作るものの一覧

Notionに作るデータベース「問い合わせ対応」のプロパティです。

プロパティ型n8nから入れる値
件名タイトル会社名とお名前をつないだもの
受付日時日付フォームの送信日時
会社名テキストフォームの会社名
お名前テキストフォームのお名前
メールアドレスメールフォームのメールアドレス
種類セレクトサービスについて/お見積もり/採用/その他
内容テキスト問い合わせ本文(先頭2,000字)
対応状況ステータス未対応(作るときに入れる)
担当ユーザー入れない(Notionで人が選ぶ)
完了通知済みチェックボックス完了を知らせたらオン

n8n側のワークフローは3本です。

ワークフローきっかけNotionノードでやること
問い合わせ受付(既存に足す)n8n Form TriggerDatabase Page の Create で1件入れる
未対応の朝まとめSchedule Trigger(毎朝9時)Database Page の Get Many で未対応だけ読む
対応完了の知らせNotion Trigger更新されたページを受け、Update で通知済みにする

手順

Notionにデータベースを作る

  1. Notionで親ページ「業務自動化」を作る
  2. その中に、テーブルのデータベースを作り、名前を「問い合わせ対応」にする
  3. 最初からあるタイトルのプロパティの名前を「件名」に変える
  4. 上の表のとおりにプロパティを足す。型は表の名前のものを選ぶ
  5. 「対応状況」のステータスの選択肢を「未対応」「対応中」「完了」に変える
  6. 「種類」のセレクトに「サービスについて」「お見積もり」「採用」「その他」を足す

プロパティ名は、あとで式から呼ぶ名前になります。途中で名前を変えるとn8n側の設定が外れるので、ここで決めきります。

内部コネクションを作る

  1. ワークスペースのオーナーのアカウントで、Notionの開発者向けポータル(Developer portal)を開く
  2. 左の「Build」の区画にある「Internal connections」を選ぶ
  3. 「Create a new connection」を押し、名前(例: n8n 問い合わせ受付)を入れ、ワークスペースを選んで作る
  4. 「Configuration」タブを開き、内容の読み取り・更新・挿入にチェックが入っていることを確かめる。「担当」の名前を使うならユーザー情報の読み取りも付ける
  5. 同じタブにあるAPIトークンをコピーする

トークンはパスワードと同じ扱いです。漏れたら「Configuration」タブから作り直し、n8nのCredentialにも貼り直します。

データベースをコネクションに共有する

  1. Notionで親ページ「業務自動化」を開く
  2. 右上の「•••」を押し、「その他」を開いて、「コネクション」のグループにある「コネクションを追加」を選ぶ
  3. さきほど作った「n8n 問い合わせ受付」を検索して選ぶ

親ページに追加すると、その下のデータベースや子ページにも届きます。

コネクションのオーナーなら、Developer portalの「Content access」タブで「Edit access」を押し、ページやデータベースを選ぶ方法でも共有できます。どちらでも結果は同じです。

n8nにCredentialを作る

  1. n8nで「問い合わせ受付」ワークフローを開く
  2. 「問い合わせフォーム」の右の + を押し、notion で検索して「Notion」を選ぶ
  3. 操作の一覧から「Create a database page」(データベースにページを作る)を選ぶ
  4. 認証情報(Credential)の欄で「Create new credential」を選ぶ
  5. 「Internal Integration Secret」にコピーしたAPIトークンを貼り、「Save」を押す

保存時のテストは「自分が誰か」を問い合わせるだけなので、共有を忘れていても成功します。共有漏れが分かるのは、次の手順でデータベースが一覧に出ないときです。

Credentialの共有や、担当者の異動に備えた持ち方は「n8nの認証情報(Credential):OAuth2・APIキー・共有と権限」で扱っています。

問い合わせを1件入れる

  1. 「Resource」が「Database Page」、「Operation」が「Create」になっていることを確かめる
  2. 「Data Source」の一覧から「問い合わせ対応」を選ぶ
  3. 「Title」に {{ $json.company }} {{ $json.name }}様 と入れる
  4. 「Add Property」を押し、「Key Name or ID」で「受付日時」を選んで値を入れる。残りのプロパティも同じように足す(値は次の表)
  5. ノード名を「Notionに追加」に変える

2.44のNotionノードは、置いた時点で一番新しい版(3)になり、データベースではなく「Data Source」(データソース)を選びます。Notionは2025年のAPIの変更で、1つのデータベースが複数のデータソースを持てるようになりました。項目(プロパティ)の定義を持つのはデータソースのほうです。普通のデータベースはデータソースが1つなので、データベースと同じ名前で一覧に出ます。

「問い合わせフォーム」から2本の線を出し、Google SheetsとNotionの両方につなぎます。Notionの直前がフォームなので、$json でフォームの値をそのまま取れます。

Notionの型n8nの欄入れる値の例気をつけること
タイトルTitle(ノードの上部){{ $json.company }} {{ $json.name }}様空だとエラーで止まる
テキストText{{ $json.message.slice(0, 2000) }}1つの欄に2,000字まで
数値Number{{ Number($json.employees) }}数字以外を混ぜない
セレクトSelect Name or ID一覧から選ぶか {{ $json.category }}無い名前は選択肢が増える
マルチセレクトMulti Select製品A, 製品Bカンマ区切り。選択肢の名前にカンマは使えない。100個まで
ステータスStatus Name or ID一覧から「未対応」無い名前を送っても選択肢は増えない
日付Date、Include Time、Timezone Name or ID{{ $json.submittedAt }}日付だけならInclude Timeをオフ
メールEmail{{ $json.email }}200字まで
電話Phone{{ $json.phone }}200字まで
URLURL、Ignore If Empty{{ $json.url }}空ならIgnore If Emptyで飛ばす
チェックボックスCheckboxオン・オフ式なら true か false
ユーザーUser Names or IDs一覧から選ぶメールアドレスではなくNotionのユーザーIDで入れる
リレーションRelation IDs相手のページIDカンマ区切り。IDの形でない値は黙って捨てられる。100件まで
ファイル&メディアFile URLsNameとFile URLNotionノードは外にあるファイルのURLだけ。アップロードはノードからはできない
数式・ロールアップ・作成日時・ID入れられないなしNotionが値を決める

日付の「Timezone Name or ID」は初期値のままだと、n8nのワークフローのタイムゾーンで解釈されます。submittedAt は「+09:00」のような時差付きなのでずれませんが、2026-10-26 14:05 のような時差の無い文字列を入れると、n8nがアメリカ東部時間のままなら13〜14時間ずれます。n8n側を Asia/Tokyo に揃える方法は「n8nの使い方入門:最初のワークフローを作って動かす」にあります。

本文も入れたいときは「Content Type」を「Markdown」にし、「Markdown」の欄に {{ $json.message }} と入れます。Notionのページを開くと、プロパティの下に問い合わせの全文が出ます。

ここで「Execute step」を押し、Notionのデータベースに1行増えれば成功です。最後に「Publish」を押して本番に出します。

Notionノードの設定。Data Sourceに「問い合わせ対応」、Propertiesに受付日時・会社名・種類などが並んでいる
Notionノードの設定。Data Sourceに「問い合わせ対応」、Propertiesに受付日時・会社名・種類などが並んでいる

未対応だけを読み出す

  1. 新しいワークフロー「未対応の朝まとめ」を作る
  2. 「Schedule Trigger」を置き、「Trigger Interval」を「Days」、「Trigger at Hour」を「9am」にする
  3. + から「Notion」を選び、操作は「Get many database pages」(データベースのページをまとめて取る)にする
  4. 「Data Source」の一覧から「問い合わせ対応」を選び、「Return All」をオンにする
  5. 「Filter」を「Build Manually」にし、「Add Condition」を押す
  6. 「Property Name or ID」で「対応状況」を選び、「Condition」を「Equals」、「Option Name(s)」に「未対応」と入れる
  7. 「Options」の「Sort」で「受付日時」の「Ascending」(古い順)を足す
  8. ノードの「Settings」タブで「Always Output Data」をオンにする

「Return All」をオフのままだと、初期値の50件で打ち切られます。「Always Output Data」は、未対応が0件の日にも後ろのノードを動かすためです。オフのままだと0件の日はワークフローがそこで終わり、Slackに何も来ません。「何も来ない」が「0件」なのか「止まった」のか区別がつかなくなるので、0件の日も「ありません」と送る作りにします。

「Simplify」がオンのとき、1件の出力はこういう形になります。

{
  "id": "3a1f0c2e-8d4b-4c1e-9f7a-2b6d5e8c1a90",
  "name": "株式会社サンプル 山田 太郎様",
  "url": "https://www.notion.so/3a1f0c2e8d4b4c1e9f7a2b6d5e8c1a90",
  "property_受付日時": { "start": "2026-10-26T14:05:00.000+09:00", "end": null, "time_zone": null },
  "property_種類": "お見積もり",
  "property_対応状況": "未対応",
  "property_メールアドレス": "yamada@example.com"
}

プロパティは property_ の後ろにプロパティ名が付いた名前で出てきます。式では {{ $json.property_種類 }} のように書けます。タイトルは name、ページのURLは url です。

Get Manyの設定。FilterがBuild Manuallyで、対応状況がEquals 未対応になっている
Get Manyの設定。FilterがBuild Manuallyで、対応状況がEquals 未対応になっている

毎朝まとめてSlackへ送る

Notionから10件読むと、後ろのノードは10回動きます。Slackにそのままつなぐと10通届くので、間に「Code」ノードを置いて1通の文面にまとめます。

  1. Notionノードの右の + から「Code」を選ぶ
  2. 「Mode」を「Run Once for All Items」にし、言語はJavaScriptのまま次を貼る
  3. + から「Slack」の「Send a message」を選び、#問い合わせ に送る設定にする
  4. 「Message Text」に {{ $json.text }} と入れる
  5. 「Execute workflow」で試し、Slackに1通届いたら「Publish」を押す
const rows = $input.all().map(item => item.json).filter(row => row.id);
if (rows.length === 0) {
  return [{ json: { text: '未対応の問い合わせはありません' } }];
}
const lines = rows.map(row => {
  const at = (row.property_受付日時?.start ?? '').slice(0, 16).replace('T', ' ');
  return `・${at} ${row.name}(${row.property_種類 ?? '種類なし'})\n${row.url}`;
});
return [{ json: { text: `未対応の問い合わせが${rows.length}件あります\n` + lines.join('\n') } }];

filter(row => row.id) は、0件の日に「Always Output Data」が出す空のitemを外すためです。Slackノードの設定とアプリの作り方は「n8nとSlack連携:通知・投稿・ボタンで承認・スラッシュコマンド」、Schedule Triggerの時刻の決め方と、この集計を部品にして使い回す方法は「n8nの定期実行とサブワークフロー:Schedule Triggerと部品化」にあります。Codeノードの書き方そのものは「n8nのCodeノード:JavaScriptとPythonで足りないところを書く」で扱っています。

Notionで「完了」にしたら知らせる(Notion Trigger)

Notionの中の変化をきっかけに動かすのが「Notion Trigger」です。

  1. 新しいワークフロー「対応完了の知らせ」を作り、「Add first step」で notion を検索して「Notion Trigger」を選ぶ
  2. 「Event」を「Page Updated in Database」(データベースのページが更新されたとき)にする
  3. 「Data Source」で「問い合わせ対応」を選ぶ
  4. 「Poll Times」は「Every Minute」のままにする
  5. 「Fetch Test Event」を押して、最新のページが1件出ることを確かめる
  6. + から「If」を置き、条件を2つ作る。{{ $json['対応状況'] }} が「完了」と等しい(is equal to)、かつ {{ $json['完了通知済み'] }} が「is false」
  7. Ifのtrue側に「Slack」の「Send a message」を置き、「Message Text」に 対応が完了しました: {{ $('Notion Trigger').item.json['件名'] }} と入れる
  8. その後ろに「Notion」の「Update a database page」を置き、「Page」の指定方法を「By ID」にして {{ $('Notion Trigger').item.json.id }} を入れる
  9. 「Add Property」で「完了通知済み」を選び、「Checkbox」をオンにする
  10. 「Publish」を押す

Notion Triggerの出力は、Notionノードと形が違います。プロパティ名に property_ が付かず、$json['対応状況'] のようにそのままの名前で出ます。どちらのノードから来た値かで書き方を変えます。

Notion TriggerとIf・Slack・Notion(Update)の4つをつないだワークフロー
Notion TriggerとIf・Slack・Notion(Update)の4つをつないだワークフロー

Notion Triggerの性質は次のとおりです。

項目内容
仕組みNotionに通知してもらうのではなく、n8nが決まった間隔で聞きに行く(ポーリング)
間隔「Poll Times」で決める。初期値は「Every Minute」(1分ごと)
選べるきっかけ「Page Added to Database」(ページが足された)と「Page Updated in Database」(ページが更新された)の2つ
時刻の細かさNotionの時刻は分単位なので、同じ分の更新は重複を避けて1回だけ拾う
テスト「Fetch Test Event」は最新のページを1件だけ取ってくる
本番Publishするまで聞きに行かない
n8n Cloudの実行回数新しいデータが見つかったときだけ1回と数える。何も無い確認は数えない

「Page Updated in Database」は、どのプロパティが変わっても動きます。n8nが自分で「完了通知済み」をオンにした更新も「更新」なので、次の確認でもう一度拾われます。手順6の条件に「完了通知済みがfalse」を入れているのは、この2回目を止めるためです。この条件が無いと、完了したページを誰かが少し直すたびに「完了しました」が届きます。

応用

できないことと、その代わり

できないこと理由代わりの手
Notionの変更を即座に拾うNotion Triggerはポーリングで、初期値は1分ごと。Notionの時刻も分単位で記録されるNotionのWebhook(コネクションの「Webhooks」タブ)からn8nの「Webhook」ノードに送る。届くのはページIDなどだけなので、Notionノードの「Get」で中身を取り直す。届くのは多くが1分以内、遅くとも5分以内で、順番が入れ替わることや、同じイベントが再送されることがある
ステータスの選択肢をNotionノードで増やすページの作成・更新では選択肢が作られず、ノードにはデータソースの項目を変える操作が無いNotionの画面で先に作る。自動で足したいときは、HTTP Requestでデータソースのプロパティを更新するAPIを呼ぶ
数式・ロールアップ・作成日時・IDに書くNotionが値を決める読み取り専用の型元になるプロパティのほうに書く
2,000字を超える文章を1つのテキスト欄に入れるNotionの上限が1つの文字列につき2,000字で、Notionノードは1つの文字列で送る欄には slice(0, 2000) で先頭だけ入れ、全文は本文(Markdown)に入れる。欄に全部入れたいときは2,000字ずつに分けてHTTP Requestで送る(100個まで並べられる)
Notionノードでファイルをアップロードしてファイル&メディアに入れるノードは外部URLの指定だけに対応(Notion API自体はアップロードに対応)Google Driveなどに置いてURLを入れる。Notionに置きたいときは、HTTP RequestでNotionのファイルアップロードのAPIを呼び、その結果をファイル&メディアに入れる
片方向のリレーションを出力に出すNotionノードは双方向のリレーションだけ表示するNotionでリレーションの設定から相手側にも表示する形にする
トグル見出しを作るノードが未対応普通の見出しを作ったあと、HTTP Requestで is_toggleable を付ける
件数・種類別の集計Notionのデータベースは集計の出力に向かないGet Manyで読んでCodeで数えるか、スプレッドシート側で集計する

よく詰まるところ

症状原因直し方
Credentialのテストは通るのに、Data Sourceの一覧が空データベースをコネクションに共有していない親ページの「•••」→「その他」→「コネクションを追加」で共有する
404 の object_not_found で止まる同上。または別のコネクションのトークンを使っている共有先とCredentialのトークンが同じコネクションか確かめる
「Title is required to create a database page」で止まる「Title」が空。フォームの項目が届いていない前のノードの出力を見て、Titleの式が値を拾っているか確かめる
400 の validation_error で止まる型の違う値、または空の値を送っている(空のメールアドレスなど)空のときは「If」で分けてそのプロパティを送らない。URLは「Ignore If Empty」をオンにする
長い問い合わせだけ失敗するテキストの欄が2,000字を超えた{{ $json.message.slice(0, 2000) }} にし、全文は本文に入れる
ステータスが入らない、エラーになるNotionに無い選択肢の名前を送ったNotionで選択肢を作る。名前の全角・半角まで揃える
「種類」に「お見積り」「見積もり」が増えていくセレクトは無い名前を送ると選択肢を自動で作るn8n側で値を固定の4つに揃えてから送る
受付日時が13〜14時間ずれる時差の無い文字列を、ニューヨーク時間のn8nが解釈したワークフローのタイムゾーンを Asia/Tokyo にし、時差付きの値を渡す
何百件もまとめて入れると途中で 429 の rate_limitedNotionのレート制限。Business・Enterpriseプラン以外は1コネクションあたり平均で毎秒3回。2.44のNotionノードはCreateのたびに項目の一覧も読むので、1件で2回使う「Loop Over Items」で10件ずつに分け、「Wait」で10秒待たせる。Notionノードの「Settings」で「Retry On Fail」をオンにし、「Max. Tries」を5、「Wait Between Tries (ms)」を5000にする
リレーションに入れたはずの値が空IDの形でない値(ページのURLなど)は黙って捨てられるURLの末尾の32文字のIDだけを渡す
ユーザーにメールアドレスを入れたら入らないユーザー型はNotionのユーザーIDで指定する「User」の「Get Many」でIDを調べるか、一覧から選ぶ
未対応が0件の日に朝のまとめが来ないGet Manyの結果が0件で、後ろのノードが動かないNotionノードの「Settings」で「Always Output Data」をオンにする
Notion Triggerが本番で動かないPublishしていない。または1分待っていないPublishし、Executionsタブで1〜2分後の実行を確かめる
「完了しました」が何度も届くn8n自身の更新も「Page Updated」として拾っている通知済みのチェックボックスを付け、条件で外す
古いワークフローの出力で日本語のプロパティが property_ 1つにまとまるテンプレートなどから持ってきた古い版(2系)のNotionノードは、英数字以外の名前を潰してしまうノードを置き直して版3にする
Notionでデータソースを1つ足したら、古いワークフローが止まった古い版のノードは、データソースが2つ以上あるデータベースを扱えないノードを置き直して版3にし、「Data Source」を選び直す

次にやること

  • n8nとGoogleスプレッドシート連携:読み書き・更新・重複を防ぐ
  • n8nとSlack連携:通知・投稿・ボタンで承認・スラッシュコマンド
  • n8nの定期実行とサブワークフロー:Schedule Triggerと部品化
  • n8nの使い方入門:最初のワークフローを作って動かす

あわせて読む