n8nで外部サービスとつなぐ:Webhook・HTTP Request・API

公開 書いた人 大林 諒

n8nと外のサービスのやり取りは3通りしかありません。相手から送ってもらう(Webhook)、こちらから呼びに行く(HTTP Requestか専用ノード)、外からn8nを操作する(n8nのPublic API)です。この記事では、問い合わせフォームの内容をGoogleスプレッドシート「問い合わせ一覧」に残してSlackの #問い合わせ に知らせる「問い合わせ受付」ワークフローを例に、3通りを全部通します。前提は、n8n 2.44.7(2026年10月時点の安定版)がn8n.example.co.jpのような独自ドメインとHTTPSで動いていることです。

この記事の内容
  1. 先に決めること
  2. この記事で作るもの
  3. Webhookで受ける
  4. 受けたデータを整える
  5. HTTP Requestで呼ぶ
  6. 例:LINEに知らせる
  7. 例:kintoneにレコードを追加する
  8. 例:Salesforceにリードとして登録する
  9. n8n自体のAPIで外から操作する
  10. 応用
  11. できないことと、その代わり
  12. よく詰まるところ
  13. 次にやること

先に決めること

専用ノードか、HTTP Requestか、Codeか

SlackやGmailのようにサービス名がついた専用ノードがあればそれを使い、無ければHTTP Requestで相手のAPIを直接呼び、それでも書けない処理だけCodeノードに回します。

状況使うもの理由
専用ノードがあり、やりたい操作が一覧にある専用ノード認証の画面が用意されていて、項目を選ぶだけで済む
専用ノードはあるが、操作が無いHTTP Request(Predefined Credential Type)専用ノードと同じ認証をそのまま使える
専用ノードが無い(LINE Messaging API・kintoneなど)HTTP Request(Generic Credential Type)APIドキュメントどおりに組める
署名の比較など、ノードの組み合わせで書けないCode最後の手段

送ってもらうか、取りに行くか

相手が「何か起きたらこのURLに知らせる」仕組みを持っているなら、送ってもらうほうが速く、n8nの負荷も小さくなります。持っていないときだけSchedule Triggerで取りに行きます。

鍵はCredentialsに置く

APIキーやトークンはノードの欄に書かず、Credentials(認証情報)に登録して選びます。直書きが残ると、鍵を変えるときに全部のノードを探して回ることになります。

この記事で作るもの

ノード役割主な設定
Webhookフォームから受けるPOST、Path: inquiry、Header Auth
Edit Fields (Set)受けたデータを整える会社名・氏名・メール・本文・受付日時
Google Sheets「問い合わせ一覧」に1行足すAppend Row
Slack#問い合わせ に知らせるMessage の Send
Respond to Webhookフォームに返事を返すRespond With: JSON
HTTP Request(応用)LINE・kintoneに送るGeneric Credential Type
Salesforce(応用)リードとして登録するLead の Create
完成した「問い合わせ受付」ワークフローの全体
完成した「問い合わせ受付」ワークフローの全体

Webhookで受ける

Test URLとProduction URL

Webhookノードには2つのURLが出ます。違いを知らないと、ほぼ確実に一度は詰まります。

Test URLProduction URL
形(初期値)https://n8n.example.co.jp/webhook-test/inquiryhttps://n8n.example.co.jp/webhook/inquiry
受け付ける間「Listen for test event」(テストイベントを待つ)を押してから120秒ワークフローを公開(Publish)している間ずっと
受けたデータエディタの画面に出る画面には出ない。「Executions」(実行履歴)で見る

n8n 2.x では、1.x で「有効化(Activate)」と呼んでいた操作が「公開(Publish)」になっています。

手順

  1. 新しいワークフローを作り、名前を「問い合わせ受付」にする。
  2. 最初のノードに「Webhook」を追加する。
  3. 「HTTP Method」を POST にする。
  4. 「Path」を inquiry にする(初期値はランダムな文字列)。
  5. 「Respond」を「Using ‘Respond to Webhook’ Node」にする。
  6. 「Listen for test event」を押す。
  7. 120秒以内に、手元のターミナルから下のcurlを送る。
  8. 右側のOUTPUTに、送った内容が出たことを確かめる。
curl -X POST 'https://n8n.example.co.jp/webhook-test/inquiry' \
  -H 'Content-Type: application/json' \
  -d '{"company":"株式会社サンプル","name":"山田 太郎","email":"taro@example.com","message":"料金表を送ってください"}'
Webhookノードの設定画面(Test URLとProduction URLが並んで見えている状態)
Webhookノードの設定画面(Test URLとProduction URLが並んで見えている状態)
Webhookノードの設定画面(Test URLとProduction URLが並んで見えている状態)

受けたデータは body(中身)、headers、params(URLの中の変数)、query(URLの ? 以降)に分かれて入ります。会社名は $json.company ではなく $json.body.company です。ここを間違えて後ろのノードが全部空になる例をよく見ます。手元のPCで動かしているn8nには外のフォームから届かないので、試すだけならcurlで自分に送れば足ります。

Webhookノードの出力。body の下に company・name・email・message が並んでいる状態
Webhookノードの出力。body の下に company・name・email・message が並んでいる状態

Header Authで鍵をかける

Production URLは、URLを知っていれば誰でも送れます。Webhookノードの「Authentication」には「Basic Auth」「Header Auth」「JWT Auth」「None」があり、フォームから送るならHeader Authが扱いやすいです。

  1. 「Authentication」を「Header Auth」にする。
  2. 新しいCredentialを作り、「Name」に X-Form-Token、「Value」に長いランダムな文字列を入れる。
  3. curlに -H 'X-Form-Token: (同じ文字列)' を足して送り、通ることを確かめる。
  4. ヘッダーを外して送り、拒否されることも確かめる。
Header AuthのCredential作成画面(Valueは伏せる)
Header AuthのCredential作成画面(Valueは伏せる)

この鍵をブラウザで動くJavaScriptに書いてはいけません。ページのソースを見れば読めるからです。フォームはいったん自社サイトのサーバーで受け、サーバー側からn8nへ送ります。PHPなら次の形で、鍵はサーバーの環境変数に置きます。

<?php
$payload = json_encode([
  'company' => $_POST['company'] ?? '',
  'name'    => $_POST['name'] ?? '',
  'email'   => $_POST['email'] ?? '',
  'message' => $_POST['message'] ?? '',
], JSON_UNESCAPED_UNICODE);

$ctx = stream_context_create(['http' => [
  'method'        => 'POST',
  'header'        => "Content-Type: application/json; charset=utf-8\r\n"
                   . "X-Form-Token: " . getenv('N8N_FORM_TOKEN') . "\r\n",
  'content'       => $payload,
  'timeout'       => 10,
  'ignore_errors' => true,
]]);
$result = file_get_contents('https://n8n.example.co.jp/webhook/inquiry', false, $ctx);

運用しているn8nで、サーバーに残したバックアップファイル(.bak)からWebhookのURLが誰でも読める状態になっていたことがあります。送信元のサーバーが決まっているなら、「Options」の「IP(s) Allowlist」も足します。n8nがリバースプロキシの後ろにいるときは、環境変数 N8N_PROXY_HOPS にプロキシの段数を入れないと、許可したIPからでも弾かれます。

Respond to Webhookで返事を返す

  1. ワークフローの最後に「Respond to Webhook」を追加する。
  2. 「Respond With」を「JSON」にし、{"ok": true} と入れる。
  3. 「Add Option」から「Response Code」を足し、200 にする。
Respond to Webhookノードの設定(Respond With: JSON、Response Code: 200)
Respond to Webhookノードの設定(Respond With: JSON、Response Code: 200)

Respond to Webhookを通らずに終わると標準のメッセージが200で、その前でエラーになると500で返ります。2個目のRespond to Webhookは無視されます。送信後に完了ページへ移したいときは「Respond With」の「Redirect」を使います。

受けたデータを整える

Webhookの直後に「Edit Fields (Set)」を1つ置き、ここで形を決めます。

  1. Webhookの後ろに「Edit Fields (Set)」を追加し、「Mode」を「Manual Mapping」にする。
  2. 左の入力から body の各項目を「Fields to Set」へドラッグする。
  3. 項目名をスプレッドシートの列名に合わせて「会社名」「氏名」「メール」「本文」に直す。
  4. 「受付日時」を新しく足し、値を式にする。
項目式ねらい
会社名{{ $json.body.company.trim() }}前後の空白を落とす
氏名{{ $json.body.name.replace(/[\t\r\n]+/g, ' ').trim() }}タブ・改行を半角スペースにする
受付日時{{ $now.setZone('Asia/Tokyo').format('yyyy-LL-dd HH:mm') }}日本時間で読める形にする
Edit Fields (Set)で項目を作った状態
Edit Fields (Set)で項目を作った状態

氏名のタブと改行を消すのには理由があります。運用しているn8nで、氏名にタブが混ざった1件だけ後ろの突合ノードが落ち、直して流し直したら通知が二重に飛びました。人が手で入れる欄は最初に掃除します。

日付はLuxonという日付ライブラリで扱います。文字列の日付は DateTime.fromISO('2026-10-26T09:00:00') で日付に読み直してから計算します。n8nの時刻の基準は何も設定しないとニューヨーク時間なので、サーバー全体は環境変数 GENERIC_TIMEZONE=Asia/Tokyo、ワークフローごとは設定画面のタイムゾーンで日本時間にします。

配列が1件ずつに分かれる仕組み

n8nはノード間で「アイテム」の並びを渡し、アイテムが3つあれば後ろのノードは3回動きます。APIから {"records": [ ... ]} のように配列が1つのアイテムで返ったときは、「Split Out」ノードの「Field to Split Out」に records を入れると1件ずつに分かれます。逆に意図せず分かれてSlackに何十回も通知が飛ぶときは、そのノードの「Settings」で「Execute Once」(最初のアイテムで1回だけ実行)をオンにします。

HTTP Requestで呼ぶ

APIドキュメントの読み方

どのサービスのAPIドキュメントでも、見る場所は同じです。

見る場所HTTP Requestの欄例(LINEのプッシュメッセージ)
メソッドとURL「Method」「URL」POST https://api.line.me/v2/bot/message/push
認証「Authentication」Authorization: Bearer {チャネルアクセストークン}
ボディ「Send Body」JSONで to と messages
次ページの取り方Optionsの「Pagination」送信APIなので無し
レート制限とエラー「Retry On Fail」と「Batching」2,000リクエスト/秒、429で上限超過

cURLを取り込む

  1. HTTP Requestノードを追加する。
  2. 「Parameters」タブの「Import cURL」を押す。
  3. 「Import cURL command」の欄にAPIドキュメントのcurlを貼り、「Import」を押す。
  4. 取り込まれたトークンをノードから消し、Credentialに移す。

取り込むとノードの設定は上書きされ、値はすべて文字列になります。数値や true/false を保ちたいときは、ボディを「Using JSON」に切り替えて書き直します。

Import cURL commandにLINEのcurlを貼った状態
Import cURL commandにLINEのcurlを貼った状態

認証のしかた

「Authentication」で「Generic Credential Type」を選ぶと、次の型が使えます。

型使う場面入れるもの
Header AuthAPIキーを独自のヘッダーで送る(kintoneなど)Name と Value
Bearer AuthAuthorization: Bearer トークン(LINEなど)Bearer Token
Query AuthAPIキーをURLの ?key= で送るName と Value
OAuth2 APIログイン画面を通して許可をもらうGrant Type・各URL・Client ID・Client Secret・Scope

OAuth2の「Grant Type」は「Authorization Code」「Client Credentials」「PKCE」から選び、相手のサービスに登録するリダイレクト先には、Credentialの画面に出る「OAuth Redirect URL」(セルフホストなら https://n8n.example.co.jp/rest/oauth2-credential/callback)を使います。

Generic Credential Typeの型の一覧が出ている状態
Generic Credential Typeの型の一覧が出ている状態

ページネーション

一覧を取るAPIは、100件ずつのように区切って返します。「Add Option」から「Pagination」を足すと、n8nが続きを取りに行きます。

Pagination Mode使う場面設定例
Update a Parameter in Each Requestページ番号をパラメータで渡すAPIType: Query、Name: page、Value: {{ $pageCount + 1 }}
Response Contains Next URL返事に次ページのURLが入るAPINext URL: {{ $response.body["next-page"] }}

$pageCount は取得済みのページ数で0から始まるので、1ページ目から数えるAPIには +1 します。$response には前回の返事の body・headers・statusCode が入ります。

ページ送りは効いていなくてもエラーになりません。運用しているn8nでkintoneから一覧を取ったとき、offset を独立したパラメータで渡していたため黙って無視され、同じ500件が何度も返っていました。kintoneは query という文字列の中に limit 500 offset 500 のように書く仕様で、limitを書かないと100件で止まり、offsetは10,000件までです。2ページ目の中身が1ページ目と違うことを必ず確かめます。

エラー時のリトライ

設定場所効き方
Retry On Failノードの「Settings」失敗したら「Max Tries」回まで、「Wait Between Tries (ms)」ずつ待って繰り返す
BatchingHTTP Requestの「Options」「Items per Batch」件ずつ「Batch Interval (ms)」空けて送る。1000 なら1秒に1回
On Errorノードの「Settings」「Stop Workflow」「Continue」「Continue (using error output)」

429が出るならBatchingで間隔を空け、それでも出るときにRetry On Failを足します。On Errorを「Continue」にするのは慎重に決めます。運用しているn8nで、kintoneから顧客を探すノードをContinueにしていたら、検索が失敗したときも「該当なし」と同じ空の結果が流れ、実在する顧客からの返信を黙って取りこぼしていました。失敗を扱うなら「Continue (using error output)」にして、エラーの出口にSlack通知をつなぎます。

Settingsタブ(Retry On Failをオンにした状態)
Settingsタブ(Retry On Failをオンにした状態)

例:LINEに知らせる

n8nの「Line」ノードはLINE Notify用で、n8n 1.64.0から非推奨です。LINE Notify自体が2025年3月31日でサービスを終えていて、もう動きません。LINEに送るなら、Messaging APIをHTTP Requestで呼びます。

  1. 「Bearer Auth」のCredentialを作り、チャネルアクセストークンを入れる。
  2. 「Method」を POST、「URL」を https://api.line.me/v2/bot/message/push にする。
  3. 「Send Body」をオンにし、「JSON」の「Using JSON」で下を入れる。
{
  "to": "{{ $vars.LINE_TO }}",
  "messages": [
    {
      "type": "text",
      "text": {{ JSON.stringify('新しい問い合わせ\n' + $json['会社名'] + ' ' + $json['氏名']) }}
    }
  ]
}

to は友だち追加した人の userId で、Messaging APIのWebhookで届くイベントから取ります。messages は1回5件まで。文字を JSON.stringify で包むのは、" や改行でJSONが壊れるのを防ぐためです。ブロックされている相手に送っても200が返り、実際には届きません。月に送れる通数の上限を超えたときも429が返ります。リトライで二重に届くのを防ぐなら、X-Line-Retry-Key ヘッダーにメッセージごとに固定したUUIDを入れます。24時間以内の同じキーの再送は409で弾かれますが、24時間を過ぎると新しい送信として届きます。

例:kintoneにレコードを追加する

kintoneには専用ノードが無いので、HTTP Requestで「1件のレコードを登録する」APIを呼びます。

  1. kintoneのアプリでAPIトークンを作り、レコード追加の権限を付ける。
  2. 「Header Auth」のCredentialで、Nameに X-Cybozu-API-Token、Valueにトークンを入れる。
  3. 「Method」を POST、「URL」を https://example.cybozu.com/k/v1/record.json にする。
  4. 「Send Body」の「Using JSON」に下を入れる。
{
  "app": 12,
  "record": {
    "会社名": { "value": "{{ $json['会社名'] }}" },
    "氏名": { "value": "{{ $json['氏名'] }}" },
    "問い合わせ内容": { "value": {{ JSON.stringify($json['本文']) }} }
  }
}

record の中のキーはフィールドコードで、例はフィールドコードを日本語で付けたアプリの場合です。存在しないフィールドコードを書くとエラーにならず、その項目だけ空欄でレコードが登録されます。一覧の取得は1回500件までで、「全件取って、無ければ督促する」作りは打ち切りに気づかず誤判定します。運用しているn8nでこれを踏み、督促が数十件誤って飛んだことがあります。アプリの作り方やAPIトークンの権限は、姉妹サイトのkintone自作ガイドに書いています。

例:Salesforceにリードとして登録する

Salesforceには専用ノードがあります。「Resource」を「Lead」、「Operation」を「Create」にして、会社名・氏名・メールを割り当てます。接続はCredentialで「OAuth2」を選び、Salesforce側はアプリケーションマネージャーの「New External Client App」で作ります。コールバックURLにn8nのOAuthのURLを入れ、スコープに「Full access (full)」と「Perform requests at any time (refresh_token, offline_access)」を足します。従来のConnected Appは廃止に向かっています。本番かSandboxかはCredentialの「Environment Type」で選びます。項目設計や権限は、姉妹サイトのSalesforce自作ガイドに書いています。

SalesforceノードでResource: Lead、Operation: Createを選んだ状態
SalesforceノードでResource: Lead、Operation: Createを選んだ状態

n8n自体のAPIで外から操作する

n8nのPublic API(REST API)で、ワークフローの一覧を取る、公開を切り替える、実行履歴を見る、ができます。セルフホストはどの版でも、n8n Cloudは有料プランで使え、無料トライアルでは使えません。

  1. 「Settings」の「n8n API」を開き、「Create API key」を押す。
  2. 「Label」に用途を入れ、「Expiration」で期限を決め、「Scopes」で範囲を選ぶ。
  3. 表示されたキーをパスワード管理ツールに保存する。
Settings > n8n API でAPIキーを作成する画面(キーは伏せる)
Settings > n8n API でAPIキーを作成する画面(キーは伏せる)

キーは X-N8N-API-KEY ヘッダーで送ります。下は公開中のワークフロー一覧と、エラーで終わった実行の一覧を取る例です。1回100件、limit で最大250件まで返り、続きは返事の nextCursor を cursor= に付けて取ります。

curl -s 'https://n8n.example.co.jp/api/v1/workflows?active=true&limit=250' \
  -H "X-N8N-API-KEY: $N8N_API_KEY"

curl -s 'https://n8n.example.co.jp/api/v1/executions?status=error&limit=100' \
  -H "X-N8N-API-KEY: $N8N_API_KEY"
やりたいことメソッドとパス
一覧・1本の中身GET /api/v1/workflows・/api/v1/workflows/{id}
公開する・やめるPOST /api/v1/workflows/{id}/publish・/unpublish
実行履歴GET /api/v1/executions(status・workflowId・includeData)
失敗した実行をやり直すPOST /api/v1/executions/{id}/retry

1.x の /activate は非推奨で /publish に置き換わりました。ワークフローを外から「今すぐ実行」するエンドポイントは無いので、外から動かしたいワークフローは先頭をWebhookにします。

APIキーの「Scopes」は「All」(全部)、「Read only」(読むだけ)、「Custom」(選んだ権限だけ)から選べます。一覧を取るだけなら「Read only」にし、「All」は作った人の権限を全部持つので管理者のパスワードと同じ扱いにします。用途ごとにキーを分けます。PUT /api/v1/workflows/{id} はワークフロー全体を置き換えるので、手元のJSONが古いと画面で直した分が巻き戻ります。送る前に本番との差分を見ます。使わないなら、セルフホストは N8N_PUBLIC_API_DISABLED=true でAPIごと止められます。

応用

よく使うサービスのつなぎ方

サービスつなぎ方押さえどころ
Slack専用ノード(Send)送るだけならボットのトークンで足りる。検索はユーザーのトークンが要る
Googleスプレッドシート専用ノード(Append Row)「Map Automatically」で項目名と列名が同じものを自動で入れる
Notion専用ノード(Database Page の Create)書き込み先のページで「Connect to」から連携を追加しないとエラー
Discord専用ノード(Message の Send)「Send and Wait for Response」で返事も待てる

スプレッドシートの行を更新するときは、日時のような値を照合のキーにしません。運用しているn8nで、同じ日時の行が2つあったため別の行を上書きし続け、3か月以上予定の処理が1件も動かなかったことがあります。公式ドキュメントには載っていませんが、更新系の操作では照合する列にn8nが付ける row_number を選べるので、行の特定にはこれを使います。

時間のかかる処理は先に返事をする

つなぐ処理が増えるほど返事は遅れます。n8n Cloudでは100秒以内に返事をしないと524で切られます。「Respond」を「Immediately」にするか、Respond to WebhookをWebhookの直後に置いて先に返し、重い処理はその後ろで続けます。

送ってきた相手を署名で確かめる

署名を付けて送ってくるサービスは、HMACを計算して確かめます。Codeノードの require('crypto') は、n8n Cloudと、セルフホストの導入記事で組んだ外部モード(task-runners を別に立てる構成)なら最初から使えます。npx n8n などで外部モードにしていない場合は、組み込みモジュールを許可する設定(NODE_FUNCTION_ALLOW_BUILTIN)を足さないと落ちます。どちらでも動くように、運用しているn8nでは計算を「Crypto」ノードに任せ、Codeノードでは比べるだけにしています。

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

できないこと代わりの手
Public APIでワークフローを今すぐ実行する先頭をWebhookにしてHTTPで呼ぶ
同じPathとメソッドで2本のWebhookを置くPathを分ける
Webhookで16MiBを超えるデータを受けるセルフホストは N8N_PAYLOAD_SIZE_MAX で上げる。ファイルはストレージに置いてURLだけ送る

よく詰まるところ

症状原因直し方
Test URLでは動いたのに、フォームから送ると404公開していない、またはURLが webhook-test のまま公開して、URLを webhook に直す
Webhookノードに出るURLが localhost外向きのURLを教えていない環境変数 N8N_WEBHOOK_URL=https://n8n.example.co.jp/ を入れて再起動(旧名 WEBHOOK_URL は2.35.0から非推奨)
後ろのノードで会社名が空$json.company と書いている$json.body.company にする
Slackに同じ通知が何十回も飛ぶ配列が分かれてアイテムが増えた通知ノードの「Execute Once」をオンにする
「The value in the “JSON Body” field is not valid JSON」本文の " や改行でJSONが壊れた値を {{ JSON.stringify(...) }} で包む
日本語が文字化けする送信側が文字コードを付けていないContent-Type: application/json; charset=utf-8 で送る
429が返る相手のレート制限Batchingで間隔を空け、Retry On Failを足す
n8n Cloudで524が返る100秒以内に返事をしていない先に返事を返し、重い処理は後ろに回す
「Connection refused (ECONNREFUSED)」Dockerの中から localhost を呼んでいる同じComposeのサービス名で呼ぶ。ホスト側は host.docker.internal(Linuxは extra_hosts に host.docker.internal:host-gateway を足す)
403「Forbidden – perhaps check your credentials」権限やスコープが足りないスコープを見直す。Notionはページに連携を追加する
2ページ目以降が取れないページ送りの指定が仕様と違い、黙って無視されているAPIドキュメントどおりの場所に書く
0件のはずが実は取りこぼしていた上限で打ち切られた、またはContinueで失敗が空の結果になった件数が上限ちょうどなら警告を出す。失敗はエラー用の出口に流す
Executionsタブで失敗した実行を開き、止まったノードが赤く出ている状態
Executionsタブで失敗した実行を開き、止まったノードが赤く出ている状態

詰まったら、Executionsタブで該当の実行を開き、ノードごとの件数と最後に動いたノードを見ます。

次にやること

あわせて読む