Slack承認フロー、「なぜか動かない」で詰まっていませんか

Slackにボタン付きメッセージを送って、押したら次の処理へ——。シンプルに聞こえるこのフロー、実際に組もうとするとハマるポイントが3箇所ある。「Slack Triggerでボタンのイベントを受け取ろうとしたら何も飛んでこない」「Waitノードを置かなかったらフローが即終了した」「Self-hosted環境でHTTPで試したら登録すら弾かれた」。この記事ではその3つの罠を全部回避しながら、Slackボタン承認フローを一気通貫で組み上げる手順を説明する。

この記事を読むと、n8nでSlackボタン承認フローを「ゼロから動かすまで」が完全にわかる。


💡 関連教材: n8nノーコード自動化 実践ワークフロー集(¥1,980) — Gmail/Slack/Notion/Claude連携の動くワークフロー10本を実装込みで完全解説(全35ページ)

結論:必要なのはこの4つのノードの組み合わせ

先に答えを出す。Slackボタン承認フローに最低限必要なのは以下の4種類だ。

  • Slackノード(Block KitボタンつきメッセージをPOST)
  • Waitノード(Resume URLを発行してフローを「一時停止」する)
  • Webhookノード(Slack AppのInteractivity URLとして登録し、ボタン押下Payloadを受け取る)
  • Switchノードapproved / rejected など値で分岐する)

この4つが揃って初めてフローが成立する。1つ欠けても動かない。順番に解説していく。


なぜSlack TriggerノードだけではボタンのPayloadを受け取れないのか

ここが一番誤解されている。

Slack Triggerが受け取れるイベントの範囲

n8nのSlack Triggerノードは、SlackのEvents API経由のイベントを受信する。具体的には「メッセージが投稿された」「チャンネルに参加した」といったイベントだ。

ボタン押下はこれとは別のAPIで動く。SlackのInteractivity(インタラクティブコンポーネント)は、Slack Appの設定画面の「Interactivity & Shortcuts」に登録した専用のURLにPOSTリクエストを送る仕組みになっている。Slack TriggerノードのURLとは全く別物だ。

つまり構造的に、ボタン押下のPayloadはWebhookノードでしか受け取れない

Waitノードがない場合に何が起きるか

実際にやってみて驚いたのだが、WaitノードなしでSlackメッセージを送信するだけのフローを組むと、「メッセージを送信した瞬間にフローが完了扱いになる」。

ボタンをユーザーが押すのは数分後・数時間後かもしれない。その間フローを「生きたまま待機させる」ための仕組みがWaitノードだ。WaitノードはResume URLというエンドポイントを発行する。そのURLにHTTPリクエストが届いた瞬間、一時停止していたフローが再開する。

Waitノードの最大待機時間は1年まで設定できる。長期の稟議承認フローにも十分対応できる。

Cloud版とSelf-hosted版の違い

項目 Cloud版 Self-hosted版
Webhook URL 自動で固定HTTPS URLが発行される 自分でドメイン・HTTPS設定が必要
Interactivity登録 そのままSlack Appに貼れる ngrokやCloudflare Tunnelが必要(開発時)
Waitノードの動作 そのまま動く 同上、ただしWebhook URLと同じドメイン設定が必要
設定難易度 ★☆☆ ★★★

Self-hosted環境でローカル開発する場合、SlackはコールバックURLにHTTPS必須で、HTTPは登録時点で弾かれる。ngrokを使えばhttps://xxxxx.ngrok.ioのような一時URLを取得できるので、それをSlack Appに登録する。本番環境ではCloudflare TunnelかリバースプロキシでHTTPS化するのが現実的だ。


全体ワークフロー構成図と各ノードの役割

フロー全体像

[何らかのTrigger]
    ↓
[Slackノード:Block Kitボタン付きメッセージ送信]
    ↓
[Waitノード:Resume URL発行 → フロー一時停止]
    ↓ ← ここでユーザーがSlackのボタンを押す
[Webhookノード:ボタン押下Payloadを受信・フロー再開]
    ↓
[Switchノード:action_value で分岐]
   ↙         ↘
[承認処理]   [却下処理]
    ↓             ↓
[Respond to Webhook:Slackにレスポンス返却]

各ノードの役割一覧

ノード名 役割 注意点
Slack(Send Message) Block KitでボタンつきメッセージをPOST blocksフィールドにJSON直書きする
Wait Resume URLを発行してフローを一時停止 URLをSlackメッセージのボタンURLに埋め込む
Webhook Slack AppのInteractivity URLとして登録 HTTPSであること、パスを固定で設定
Switch action_valueの値で条件分岐 Payloadのパスを正確に指定する
Respond to Webhook Slackへ200レスポンスを3秒以内に返す これがないとSlack側でエラー表示される

SlackのInteractivityには3秒以内のレスポンスというタイムアウト仕様がある。重い処理はRespond to Webhookで先に200を返してから非同期で走らせる設計が鉄則だ。


ステップ別セットアップ手順

Step1:Slack AppにWebhook URLを登録する

  1. api.slack.com/apps にアクセスしてアプリを選択
  2. 左メニューの「Interactivity & Shortcuts」を開く
  3. InteractivityのトグルをONにする
  4. 「Request URL」フィールドに、n8nのWebhookノードのURLを貼る
    • Cloud版例:https://your-instance.app.n8n.cloud/webhook/slack-interaction
    • Self-hosted例:https://your-domain.com/webhook/slack-interaction
  5. 「Save Changes」をクリック

これでSlack上のボタンが押されるたびに、このURLにPOSTリクエストが飛んでくる。

Step2:n8nでWaitノードを設置しResume URLを取得する

Waitノードを配置したら、Resume URLの取得方法を確認する。

  • Waitノードの設定画面でResume on webhook callを選択
  • $json["resumeUrl"]という変数にResume URLが格納される(実行時に動的生成)

このURLをSlackメッセージのボタンに直接埋め込む設計が最もシンプルだ。後述のBlock Kit JSONで使う。

Step3:Block Kit JSONでボタン付きメッセージを組む

Slackノード(Send Message)のBlocksフィールドに以下のJSONを設定する。{{ $json.resumeUrl }}の部分がWaitノードのResume URLを動的に挿入する箇所だ。

{ “blocks”: [ { “type”: “section”, “text”: { “type”: “mrkdwn”, “text”: “申請が届いています\n申請者: {{ $json.requester }}\n内容: {{ $json.content }}” } }, { “type”: “actions”, “elements”: [ { “type”: “button”, “text”: { “type”: “plain_text”, “text”: “✅ 承認する” }, “style”: “primary”, “value”: “approved”, “url”: “{{ $json.resumeUrl }}?action=approved” }, { “type”: “button”, “text”: { “type”: “plain_text”, “text”: “❌ 却下する” }, “style”: “danger”, “value”: “rejected”, “url”: “{{ $json.resumeUrl }}?action=rejected” } ] } ] }


ポイントは`url`フィールドにResume URLを設定している点だ。ボタンを押すとSlackがそのURLをブラウザで開く仕組みで、同時にWaitノードへのコールバックが発生してフローが再開する。クエリパラメータ`?action=approved`で承認か却下かを後続のSwitchノードに渡す。

### Step4:WebhookノードでInteractivity Payloadを受け取る

Webhookノードの設定は以下の通り。

```text
HTTP Method: POST
Path: slack-interaction
Authentication: None(Slack署名検証を別途実装する場合はHeaderで処理)
Response Mode: Using Respond to Webhook Node

SlackからのPayloadはapplication/x-www-form-urlencoded形式で飛んでくる。payloadというキーの中にJSON文字列が格納されているので、後続ノードでJSON.parse()するか、n8nのExpressionで{{ JSON.parse($json.payload) }}と書いてオブジェクトに変換する。

実際のPayload構造の主要部分はこうなっている。

{ “type”: “block_actions”, “actions”: [ { “type”: “button”, “action_id”: “button_approve”, “value”: “approved” } ], “response_url”: “https://hooks.slack.com/actions/xxx/yyy/zzz”, “user”: { “id”: “U12345678”, “name”: “yamada.taro” } }


`response_url`は後でメッセージを上書きするために使う。必ず保持しておく。

### Step5:Switchノードで承認・却下を分岐させる

Switchノードに以下の条件を設定する。

```text
値の参照先: {{ $json.query.action }}
条件1: approved → 承認フローへ
条件2: rejected → 却下フローへ
Default: エラーハンドリングへ

Step3のURLに?action=approvedを付けていたので、WebhookノードのQueryパラメータとしてactionが取得できる。

Step6:処理後にSlackのメッセージを上書きする

承認・却下の処理が終わったら、元のSlackメッセージを「承認済み」「却下済み」の表示に更新する。response_urlにPOSTするだけで実現できる。

// n8nのHTTP Requestノードの設定
// Method: POST
// URL: {{ $json.response_url }}
// Body(JSON):
{
  "replace_original": true,
  "text": "✅ この申請は承認されました(承認者: {{ $json.approver_name }})"
}

replace_original: trueにすると、元のボタン付きメッセージが上書きされる。承認済みなのにボタンが残り続けるのを防げる。


よくある失敗と正直なデメリット

失敗1:3秒タイムアウトでSlackにエラーが出る

Slackはボタン押下から3秒以内にHTTP 200を返さないと、Slack側でエラーメッセージを表示する。重い処理(DBへの書き込み、外部API呼び出し)をResponse前に置くと確実に詰まる。

対策はRespond to Webhookノードを一番最初に置いて空の200を返し、実処理を非同期に流す設計にすること。

失敗2:WaitノードのResume URLを間違えて別フローのURLを使う

WaitノードのResume URLは実行ごとに動的生成される。ハードコードすると古いURLを参照して永遠にフローが再開しない。必ず{{ $json.resumeUrl }}で動的に参照する。

失敗3:Self-hosted環境でPayloadが届かない

HTTP環境でSlack AppのInteractivity URLを設定しようとすると、Slackの設定保存画面でバリデーションエラーになる。ngrokを使う場合もセッションが切れると新しいURLになるので、開発中は頻繁に再登録が必要になる。正直かなり面倒だ。本番環境では専用ドメイン+Let’s EncryptのHTTPS設定を最初に固めてから開発を進めるほうが効率がいい。

このアーキテクチャの正直なデメリット

  • WaitノードはCloud版の実行コストを消費する。長期待機フローを大量に走らせるとプランのクレジットに影響する
  • Slack AppのInteractivity URL変更は即時反映されないことがある。設定後5〜10分待ってから動作確認するほうが安全
  • ボタンのURL方式はモバイルSlackでの動作が不安定なケースがある。ブラウザが開く仕様のため、モバイルアプリでは体験が良くない。モバイル利用が多い場合はAction IDを使う本格的なInteractivity実装を検討する

よくある質問

Q1:承認フローで「誰が承認したか」を記録するには?

Webhookノードで受け取るPayloadのuserオブジェクトにidnameが含まれている。$json.payloadをパースした後にactions[0].user.nameで取得できる。これをGoogle SheetsやNotionのデータベースに書き込むノードを分岐後に追加すればよい。

Q2:承認が必要なメッセージを複数人に同時送り、どちらかが押したら処理したい場合は?

WaitノードのResume URLは1度コールされたらフローが再開して無効化される。複数人に送る場合、「最初の1人が押した時点で処理を進める」仕様で問題ないならそのまま使える。一方、「全員の承認が必要」なケースはカウンターをDBに持つ設計が必要になる。n8n単体で完結させるのは難しく、外部DBとの組み合わせが現実的だ。

Q3:ボタンのURLをクリックするとブラウザが開いてしまうが、Slack内で完結させたいのは?

URL方式の仕様上、ブラウザが開くのは避けられない。Slack内のみで完結させたい場合はurlフィールドではなくaction_idを使い、Slack AppのSlash CommandやInteractivity経由でPOSTさせる設計に変更する必要がある。ただしその実装はより複雑になる。手軽さを優先するならURL方式を使い、Respondページを「承認済みです。このタブは閉じてください」と表示するだけのHTMLにする運用が現実的だ。


まとめ:次にやること、1つだけ

Slack承認フローで詰まる原因の9割は「Slack TriggerとWebhookノードの役割の混同」と「Waitノードの未設置」だ。この2点さえ押さえれば、あとは手順通りに組むだけで動く。

今すぐやること:n8nにWebhookノードを1つ追加して、パスをslack-interactionに設定し、そのURLをSlack AppのInteractivity & ShortcutsのRequest URLに登録する。

これだけでボタン受信の基盤が整う。Slackメッセージ送信の細かいBlock Kit設計については「n8n Slack通知カスタマイズ」の記事も合わせて読んでほしい。Block Kitのsectionやcontextブロックの使い方を詳しく扱っている。


関連記事


📘 もっと深く学びたい方へ

この記事で紹介した内容を、さらに体系的に・実務レベルで習得できる教材を販売中です。

n8nノーコード自動化 実践ワークフロー集(¥1,980)

Gmail/Slack/Notion/Claude連携の動くワークフロー10本を実装込みで完全解説(全35ページ)

  • Cloud版前提・2026年最新のn8n v1系UI完全対応
  • 動くノード設定値・JSON・OAuth設定まで全部入り
  • 10ワークフロー(Gmail/Slack/Notion/Discord/Webhook/RSS)はコピペで即実務投入

👉 今すぐ購入する

ChatGPT業務自動化 実践テンプレート集(¥1,480)

API・スプレッドシート・メール・議事録・請求書をコピペで自動化する実装特化型テンプレート集(全22ページ)

  • 動くGASコード・API設定手順・プロンプトをワンセット収録
  • スプレッドシート連携/メール/議事録/請求書を実務レベルで自動化
  • コピペで即動く実装コード(Python / GAS)付き

👉 今すぐ購入する


関連ツール紹介

AIスキルを収益化するならココナラでサービスを出品できる。AIライティング・画像生成・データ分析など、AIスキルを活かした案件の需要は増えている。

おすすめツールの一覧はこちらにまとめている。