Notion 3.5 Developer Platform リリースから5日で「Slackに議事録を投げるだけで全社のタスクが自動で立ち上がる」を実装してみた
Worker × Custom Agent × Slack 直結連携の壁と、その超え方 ── 実装中に踏み抜いた13個の落とし穴を完全公開
はじめに:何を作ったか
2026年5月18日、私は次のシステムを動かす実装を完成させました:
Slack の #meeting -notes チャンネルに議事録を投稿する
30秒待つと、Notion の L3 Knowledge Base に議事録ページが自動作成される
さらに1分待つと、議事録から ToDo が抽出され、L1 My Tasks に複数件のタスクが Source=会議 で起票される
L1 タスクの担当者・期限・優先度・関連プロジェクトは、すべて Claude が文章から推定して埋める
これは 2026年5月13日にリリースされた Notion 3.5 Developer Platform(Notion CLI / Workers / Custom Agents 統合)の機能をフル活用した実装で、リリースから5日で完成しました。
なぜこんなものを作ったのか?
AIコンサル業を営む立場として、**「実装まで知っている人間が提案している」**という事実を商談の場で証明する必要があったからです。AIを語るのに自分の業務管理がExcelとSlackで散らかっていたら、誰も発注しません。
この記事では、その実装の全工程と踏み抜いた13個の落とし穴を公開します。Notion 3.5 で Worker + Custom Agent を組み合わせて何か作ろうとしている方への、5日分の時間短縮材料になればと思います。
完成形:3層のAIが連鎖して動く Agent Orchestra
最終的なアーキテクチャは以下のようになりました:
[1] Slack #meeting-notes チャンネル
↓ メッセージ投稿
[2] Notion Custom Agent「Meeting Splitter」
(Slack直結トリガー)
↓ 自動発火
[3] L3 Knowledge Base にページ作成
(Type=議事録、本文=Slackメッセージ全文)
↓
[4] 同じ Custom Agent が本文から ToDo を抽出
↓
[5] L1 My Tasks に複数タスク起票
(Source=会議、Assignee/Due/Priority/Project はClaudeが推定)これと並行して、外部メール(Gmail)からタスクが起票される別ルートも Worker で実装しています:
Gmail @task ラベル付与
↓
gmail-task-router Worker(webhook)
↓
L1 My Tasks に Source=メール で起票
↓
Custom Agent「Ticket Router」が発火
↓
Project と Phase を自動推定して書き戻しつまり、入口は Slack でも Gmail でも、出口(L1 My Tasks)は常に同じ。これが商談で見せると圧倒的に効きます。
全体アーキテクチャ:4DB + 2Worker + 2Agent
Notion 側のDB構造(4層モデル)

L1〜L4 が双方向リレーションでつながっているのが肝で、「タスクを開けば、所属プロジェクトと顧客と議事録に芋づる式に辿れる」 状態を作ります。
Worker(TypeScript / Notion CLI でデプロイ)

Custom Agent(Notion UI でノーコード設定)

合計4DB + 2Worker + 2Agent。
1日でここまで動かせます。
Phase 1:Notion Business 契約と Workers 有効化
まず Notion Business プランの契約から。Workers の利用には Business または Enterprise が必須です(Plus 以下では Workers の設定画面自体が出てこない)。
私は 月額¥3,800の月払い で契約しました。年払いだと月額¥3,150になりますが、いきなり年契約縛りはリスキーなので、1ヶ月でROIを確認してから判断する方針です。
契約直後のチェックリスト
Settings → Plans → Upgrade → Business
Settings → Connections → Developer Platform → 「Enable Workers」を ON
Settings → Billing → 領収書設定(会社名・住所・インボイス登録番号)
Pitfall #1: ベータ期間の終了日
スクリーンショットで見て初めて知ったのですが、Workersが無料で使えるベータ期間は 2026年8月11日まででした。9月以降は「Notion クレジット」(金額未公表のアドオン)が必要になります。
💡 ベータ期間中に最大限実装と検証を済ませる戦略 ── 今日が5月18日なので残り85日しかないと知った時の焦りで、私は実装を一気に進めました。
Phase 2:4DB自動構築(Node.jsスクリプトで一発)
4つのDBを手動で作るのは時間の無駄なので、Node.jsスクリプトでNotion APIを叩いて一発構築しました。CSV 4本を読み込んで、DB作成 → プロパティ設定 → Select選択肢登録 → 行挿入 → Relation解決まで自動化。
javascript
// 抜粋: L4 → L2 → L3 → L1 の順で作成
const l4Db = await createDB('L4 Clients & Deals', {
'Client Name': { title: {} },
Stage: { select: { options: [
{ name: 'リード', color: 'gray' },
{ name: '商談', color: 'yellow' },
{ name: '受注', color: 'green' },
]}},
// ...
});
// L4 行挿入後、L2 を作成(L4 へのRelation付き)
const l2Db = await createDB('L2 Projects & Cases', {
Client: { relation: { database_id: l4Db.id, single_property: {} } },
// ...
});Pitfall #2: single_property vs dual_property
Notion API で Relation を作るとき、single_property と dual_property の2種類があります:
single_property:片方向リレーション(L1から L2が見えるが、L2 から L1 は見えない)
dual_property:双方向リレーション(両方から相手が見える)
私は深く考えずに single_property で作ってしまいました。
結果、L2 プロジェクトのページを開いても、紐づくタスク25件が表示されない状態に。これが商談デモで致命的:「タスクからプロジェクトへ芋づる式に辿る」ストーリーが成立しないからです。
対処:Notion UI で各 Relation プロパティを開き、「他のDBに表示」トグルをONにする。
これはAPI側からdual_propertyとして作るのが正解で、次回からはスクリプトを書き直します。
Pitfall #3: CSV内のProject名がL2と完全一致せずRelation解決失敗
CSVデータの設計でも罠を踏みました。L1 タスクの CSV に Project = "Notion全社展開PJ" と書いていたのに、L2 のProject Name は "Notion全社展開PJ - サンプル製造" と suffix 付き。これが完全一致しないため、import スクリプトが25件分のRelation解決に失敗。
対処:別途 fix_relations.js という後付けスクリプトを書いて、CSVの Project 名 → L2 の正式名 への補正マップを適用。
javascript
const PROJECT_NAME_FIX = {
'Notion全社展開PJ': 'Notion全社展開PJ - サンプル製造',
};学び:CSVデータを作る時点で、L2 と完全一致する名前を使う。これだけです。
Phase 3:ビュー設計 ── 集約率ダッシュボード が商談の心臓
L1 My Tasks に「Source 別 Board ビュー」を作ります。プロパティは:
会議(経営者・部門長との議論で出たタスク)
メール(外部からの受信由来)
Slack(チャット流れから拾われたタスク)
顧客(直接の依頼・問い合わせ)
自発(自分から動いたもの)

これが Board 形式で並ぶと、商談で次のセリフが完璧にハマります:
「導入前は 8割のタスクがメール埋もれ・Slack流れ・口約束で、誰が何を抱えているか経営者は把握できませんでした。今このボード上に 全件捕捉済みです。これが『集約率』というKPIです。」
数字で語れる集約率KPIが、感覚的な「Notionを入れたら便利になりました」より100倍説得力があります。
Phase 4:Worker #1(gmail-task-router)── pitfallの嵐
ここから本格的に Notion Workers 領域に入ります。5時間で7個のpitfallを踏み抜いた実録です。
Pitfall #4: Windows ネイティブ未対応
最初の壁。https://ntn.dev/install.ps1 という PowerShell インストール用URLは存在しない(公式ドキュメントにあると私は誤認していた)。実は ntn CLI は 2026年5月時点で macOS/Linux のみネイティブ対応で、Windowsは WSL2 必須 です。
対処:WSL2 (Ubuntu) を開いて、そこで作業する。すでに WSL2 が入っている人なら 5 分で済みますが、新規インストールは PC 再起動含めて30分はかかります。
Pitfall #5: npm install -g の権限エラー
WSL2 で curl -fsSL https://ntn.dev | bash を実行したら:
error: Could not install the Notion CLI to /usr/local/bin/ntn
The installer could not create or write to /usr/local/bin.対処:エラーメッセージに書いてあった通り、ホームディレクトリにインストール:
bash
curl -fsSL "https://ntn.dev" | NTN_INSTALL_DIR="$HOME/.local/bin" bashそして PATH に追加:
bash
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrcこれは Linux慣れしてる人なら 1 分で気づきますが、Windows ユーザーには罠です。
Pitfall #6: @notionhq/workers のバージョン pin が罠
私は npm の癖で package.json に:
json
"@notionhq/workers": "^0.5.0"と書きましたが、そんなバージョンは存在しない。実際の公式 scaffold は:
json
"@notionhq/workers": ">=0.0.0"を使っています。
対処:ntn workers new ntn-scaffold で公式スキャフォルドを生成 → cat package.json で正解を確認 → 自分のプロジェクトにコピペ。
Pitfall #7: semver ^0.0.0 の特殊ルール
ここで semver の闇に遭遇しました。>=0.0.0(任意のバージョン)と書こうとして^0.0.0 と書き間違えると:

対処:キャレットを使うなと言いたいわけではなく、0.x.y で x=0 のときだけ特殊ルールがあることを覚えておく。
Pitfall #8: tsconfig.json は公式scaffold準拠が安全
私は最初、 target: ES2022, module: ESNext, moduleResolution: Bundler で書きましたが、公式scaffoldは:
json
{
"target": "ES2020",
"module": "nodenext",
"moduleResolution": "nodenext",
"outDir": "./dist",
"rootDir": "./src"
}対処:scaffold準拠に書き直し。これで build パイプラインが期待通り動きます。
Pitfall #9: build スクリプト未定義
ntn workers deploy を実行すると、内部で npm run build が走ります。が、私は package.json に build スクリプトを書いていなかったため:
[build:err] npm error Missing script: "build"対処:"build": "tsc" を scripts に追加。これも scaffold 準拠の話です。
Pitfall #10: @types/node が無いと型エラーの嵐
process.env.X を使うと TS2580: Cannot find name 'process' が出ます。Buffer、console、fetch も同様。
対処:
bash
npm install --save-dev @types/nodeこれで Node 22 ランタイムの型情報が入り、すべて解決。
Pitfall #11: WebhookConfiguration の title と description が必須
webhook を以下のように書いてエラー:
typescript
worker.webhook("onGmailTaskLabel", {
execute: async (events, context) => { /* ... */ }
});error TS2345: Argument of type ... is missing the following properties: title, description対処:title と description を追加。tool 系のCapability例(公式scaffold)にも書いてあった必須項目を webhook にも適用する必要があるとは思っていませんでした。
typescript
worker.webhook("onGmailTaskLabel", {
title: "Gmail Task Router",
description: "Gmail @task ラベルが付与されたメールを L1 My Tasks に自動起票する Worker",
execute: async (events, context) => { /* ... */ }
});Pitfall #12: Webhook callback の引数シグネチャを間違える
最大の罠でした。私が書いたコード:
typescript
execute: async ({ payload, notion }) => { /* ... */ }これは 完全に間違い。型エラー:
'(events: WebhookEvent[]) => ...' は '(events: WebhookEvent[], context: CapabilityContext)' に代入不可正しいシグネチャ:
typescript
execute: async (events, context) => {
for (const event of events) {
const payload = (event as any).payload || (event as any).body;
// 処理
await context.notion.pages.create({ /* ... */ });
}
}ポイント:
引数は2つ(events, context)、destructuringではない
events は配列(バッチ処理が前提)
Notion クライアントは context.notion から取得
戻り値は void または Promise<void>
これは型定義ファイル node_modules/@notionhq/workers/dist/capabilities/webhook.d.ts を直接見て理解しました。ドキュメントよりコード読んだ方が早い、はあるあるです。
Pitfall #13: NOTION_ プレフィックスは予約済(ただし1個だけ例外)
ntn workers env push 時のエラー:
error: Failed to set environment variables (400 Bad Request InvalidSecretError):
Environment variable name must not start with "NOTION_" (reserved prefix)私は NOTION_L1_DB_ID という名前を使っていたのが拒否。
でも矛盾:実行時のエラーには「NOTION_API_TOKEN を .env に追加してください」と書いてある。実際 context.notion は内部で NOTION_API_TOKEN を要求する。
真実:NOTION_* プレフィックスは予約だが、NOTION_API_TOKEN 1個だけは公式の例外。それ以外のカスタム変数は別の名前にする必要があります。
対処:
diff
- NOTION_L1_DB_ID=xxx
+ L1_DB_ID=xxx
+ NOTION_API_TOKEN=secret_xxxそしてコード側も process.env.L1_DB_ID に変更してデプロイし直す。
Phase 5:Custom Agent #1(Ticket Router)── ノーコードの威力
Worker と打って変わって、Custom Agent はNotion UI でノーコード設定できます。最大の発見はここでした。

Agents メニューから新規エージェント作成 → 名前・Instructions・トリガー・ツールアクセス を入力するだけ。コードを1行も書かずに「L1 にメール由来タスクが追加されたら、Projectを自動推定して埋める」AIが動きます。
Pitfall #14: Filter条件と Worker出力の不整合
私は最初、Ticket Routerのフィルタを「Source is メール AND Priority is empty」にしました。意図:「まだ未処理のメールタスクだけ拾う」。
ところが、Worker (gmail-task-router) が Priority=Mid を初期セットしてしまうため、このフィルタには 永遠にマッチしない。
対処:フィルタを「Source is メール AND Project is empty」に変更。WorkerはPriorityを埋め、Custom AgentはProject分類を担当する 役割分担にする。
これは仕様変更というより設計の役割分担を明確化した感じです。Worker は捕獲、Agent は分類、で住み分け。
Phase 6:Worker #2(slack-meeting-ingest)── Slack統合の壁
Slack を Notion に繋ぐ場面で、3つの選択肢のうち1つが死んでいることを学びました。
当初プラン:Slack App + Event Subscriptions
通常のWebhook設計なら:
Slack App を作成
Event Subscriptions で message.channels イベントを購読
Worker の Webhook URL を登録
Slack が URL Verification Challenge を送る → Worker が challenge 値を返す
ところがNotion Workers の webhook callback は void を返す設計で、HTTP レスポンスボディを返せない。Slack が期待する challenge 応答ができず、URL Verification が永遠に通らない。
Pitfall #15: 公式Worker webhook は HTTP レスポンスを返せない
これは設計上の制約で、回避策はありません。Slack の Event Subscriptions は Notion Worker と直結できないのが現実。
解決策3つ

最大の発見:Custom Agent の Slack ネイティブトリガー
ここで Notion UI を触っていて気づきました。Custom Agent のトリガー設定画面に Slack が標準で含まれている:
メッセージが投稿されました
絵文字リアクションが追加されました
エージェントがメンションされました
つまり Worker を経由せず、Custom Agent が直接 Slack を監視できる。
Slack 統合に半日溶かしかけたところでこれを発見し、slack-meeting-ingest Worker は実装したが本番運用ではバックアップ用、本番は Custom Agent 直結で行く方針に変更しました。
💡 この経験からの学び:Notion の Custom Agent と Worker は補完関係。Worker は外部APIへのactiveアクセス(Gmail API、freee API など)に強い。Custom Agent は Notion 内のpassive監視と自然言語ロジックに強い。SlackやNotion内イベントは Custom Agent 直結が筋。
Phase 7:Custom Agent #2(Meeting Splitter)── Agent Orchestra の心臓
Meeting Splitter は 2つのトリガーで起動する設計にしました:


L3 Knowledge Base にページが追加されたとき(Worker経由 or 手動で議事録追加)
Slack #meeting -notes にメッセージが投稿されたとき(Slack直結トリガー)
Pitfall #16: 重複発火の防止
両方のトリガーが有効だと、同じSlackメッセージで2回発火して L1 タスクが重複作成される懸念があります。
対処:Instructionsの中で重複判定ロジックを書く。
ケース B: Slack のメッセージが投稿された場合
→ まず L3 を検索し、過去5分以内に同じ内容のページが既にあれば
(Worker が先に作っている可能性)スキップする
→ 重複なし → L3ページ作成 → ToDo抽出 → L1起票これで両ルートが共存できます。
Instructions の全文(参考)
あなたは議事録から ToDo を抽出する専門アシスタントです。
このエージェントは2つのトリガーで起動します:
ケースA: L3 ページ追加
→ 本文から ToDo を抽出、L1 にタスク起票
ケースB: Slack メッセージ投稿
→ 重複チェック → L3 にページ作成 → ToDo抽出 → L1 にタスク起票
抽出ルール:
- 本文の「ToDo」「次回までに」「対応する」を含む箇条書きを抽出
- 担当者は文中の名前から推定("田中部長が..." → Assignee: 田中部長)
- 期限は文中の日付表現から推定(「至急」→ High、「来週」→ Mid)
- Project は L2 を検索して最も近いものをリンク
L1 への書き込み:
- Name: 簡潔に(最大40文字)
- Status: Backlog
- Priority: High/Mid/Low
- Source: 会議
- Due: 文中の期限 or 会議日+7日自然言語で判定ルールを書く のが最大の魅力。Zapier的なif-then 構築と比べて、SMB の経営者や部門長が自分で調整できるのが Notion Custom Agent の強さです。
Phase 8:エンドツーエンドテスト ── 2種類のサンプル議事録で検証
完成後、2つの異なる議事録サンプルで動作確認しました:

v1 で 6件、v2 で 7件のタスクが L1 に正しく起票されました。曖昧な期限表現も Claude が日付化できることを確認。
商談デモの実演用には v1(プロジェクト進捗)、汎用性アピール用には v2(営業フェーズ)を見せる、と使い分けると効果的です。
Pitfall完全リスト:13個の落とし穴と対処


ざっくり15個のpitfall + 2つの設計判断で完成しました。
5日間で学んだ本質的なこと
Notion 3.5 は「データ層とAI層のオーケストレーション基盤」
これまで Notion は「ドキュメント + データベース」でしたが、Workers と Custom Agent が加わって "AIを束ねるハブ" に変わりました。SMB にとっての意味は:
「Notion に情報を集約しさえすれば、AI に任せる準備が完了する」
これ以外のSaaSにこの戦略を取れているものは少なく、3年後の業務基盤として Notion 3.5 がSMBにとって筋が良い理由です。
Worker と Custom Agent は補完関係

外部から取り込みは Worker、内部処理は Custom Agent という棲み分けが綺麗に機能します。
商談で効くのは「人間が何もしていない」演出
5日かけて分かったのは、商談で1番効くのは技術の詳細ではなく "私の指は今、何も動いていません" という瞬間 ということ。

Slackにペーストして30秒待つだけで Notion に議事録ページが現れ、さらに L1 に複数タスクが上がる ── これを見せた瞬間、SMB の経営者は「自分の会社でもこれが動く絵」を頭の中で描けます。


実装の苦労(このリストの16個)は商談で語らず、結果だけを見せるのが効果的。
余談:この5日間の費用と回収

これに対するリターン候補:
Spot deployment 1件 = ¥30,000(7.9倍)
Half-day WS 1件 = ¥300,000(78.9倍)
商工会議所登壇 1件 = ¥300,000(78.9倍)
加えて:
このNote記事 → リード獲得チャネル(永続資産)
X / LinkedIn での実装スクショ投稿
商談デモの読み取り専用URL(売れない期間も使い続けられる)
¥3,800を「広告費」と見なせば、1ヶ月以内にROIプラスは確実だと判断しています。
おわりに:御社でも90日でこれが動きます
私が AI コンサルとして提供している半日ワークショップ+全社展開プログラムでは、90日後に御社で同じ仕組みが立ち上がる状態を約束しています。
Phase 0 (1週間): 現状診断・タスク棚卸し
Phase 1 (2週間): 4層DB構築・テンプレ整備
Phase 2 (4週間): チーム展開・運用定着(集約率60%目標)
Phase 3 (4週間): Claude × MCP × Worker 連携(工数削減30%目標)
Phase 4: 自走化・改善ループ
投資額は ¥850,000(税抜)。
人材開発支援助成金(事業展開等リスキリング支援コース) を併用すると、実質負担 約¥412,000 まで圧縮できます。
「自社でNotionを動かしているコンサルから、自社でNotionを動かす方法を学ぶ」という一貫性が、私の提供する価値です。
商談デモの読み取り専用URL(今回作った Notion ワークスペース)は、ご興味あれば DM ください。実物を見ながら 30 分でメリットを判断していただけます。
