← 全記事
チュートリアル

署名付きインバウンド Webhook から n8n WhatsApp AI Agent を作る

2026 年に WhatsApp AI Agent を素早く作るなら、最初に Agent を作り込むより、インバウンドの入口を固める方が実用的です。

公開コミュニティの課題ははっきりしています。最近の n8n Reddit スレッドでは、ある開発者が WhatsApp automation を DeepSeek とつなぎ、メディア処理と本番安定性を求めつつ、Meta Business verification と Cloud API セットアップで足止めされたくないと相談していました。別の議論では、WhatsApp trigger が一度しか動かない、test mode でしか動かない、webhook delivery と workflow response のタイミングが合わない、といった問題が繰り返し出ています。

小さなチームが最初に直すべきなのは順番です。メッセージを確実に受け取り、すばやく確認応答し、保存してから、AI Agent に返信するかどうかを判断させます。

UnifyPort はインバウンド部分として署名付き message.received イベントを提供します。n8n はワークフローのキャンバスを提供します。間に小さなエッジ検証サービスを置くと、本番向きの経路になります。

WhatsApp customer message
  -> UnifyPort signed message.received webhook
  -> Edge verifier for X-Device-Signature
  -> n8n production Webhook URL
  -> AI triage, CRM lookup, Slack alert, or reply via POST /v1/messages

日本やタイでは LINE が主要チャネルになることも多いですが、入口の考え方は同じです。WhatsApp で始めても、後で LINE を追加しても、最初の契約は「署名された入站イベントを保存してから処理する」です。

n8n webhook URL が重要な理由

n8n の公式 Webhook node documentation は、Webhook node には test URL と production URL の 2 種類があると説明しています。test URL はエディタが listen している間の手動テスト用です。workflow を active にした後に外部システムへ設定するのは production URL です。

顧客メッセージのパイプラインは production URL を前提に設計します。エディタが listen していなかったからといって、顧客が同じメッセージを送り直してくれるわけではありません。インバウンド経路は常に active で、安定していて、すばやく 2xx を返せる必要があります。

n8n の Respond to Webhook node は、workflow が HTTP response を制御したいときに便利です。インバウンドメッセージでは response を単純に保ちます。イベントを受け取り、キューまたはストレージに渡し、200 を返します。長い AI 推論、CRM 書き込み、外向き返信は delivery を acknowledge した後で実行します。

UnifyPort webhook を登録する

UnifyPort で webhook endpoint を作成し、message.received を購読します。signing_secret を設定すると、各 delivery に X-Device-TimestampX-Device-Signature が付きます。

curl -X POST https://api.unifyport.ai/v1/webhook-endpoints \
  -H "X-Api-Key: <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://edge.example.com/unifyport/n8n",
  "status": "active",
  "subscribed_events": ["message.received"],
  "signing_secret": "<WEBHOOK_SIGNING_SECRET>"
}'

ここでは n8n URL を直接入れません。小さなエッジ検証サービスの URL を入れます。UnifyPort は HMAC-SHA256 で raw request body に署名するため、署名検証を厳密に行うには raw bytes が必要です。署名対象の文字列は次の形です。

<X-Device-Timestamp>.<raw request body>

n8n のデプロイで JSON parse 前の完全な raw bytes を扱えるなら、n8n 内で検証しても構いません。多くのチームでは、小さなサービスで検証し、内部 token を付けて信頼済みイベントだけを n8n production webhook に転送する方が境界を保ちやすくなります。

エッジ検証サービスを追加する

次は Node.js の完全な検証サービスです。UnifyPort 署名を検証し、イベントを parse し、イベント種別を確認して、信頼済み payload を n8n に転送します。

import crypto from "crypto";
import express from "express";

const app = express();
const signingSecret = process.env.WEBHOOK_SIGNING_SECRET;
const n8nWebhookUrl = process.env.N8N_PRODUCTION_WEBHOOK_URL;
const internalToken = process.env.N8N_INTERNAL_TOKEN;

app.post("/unifyport/n8n", express.raw({ type: "application/json" }), async (req, res) => {
  const timestamp = req.get("X-Device-Timestamp") || "";
  const signature = req.get("X-Device-Signature") || "";

  const hmac = crypto.createHmac("sha256", signingSecret);
  hmac.update(timestamp);
  hmac.update(".");
  hmac.update(req.body);
  const expected = hmac.digest("hex");

  const valid =
    signature.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));

  if (!valid) {
    res.status(401).end();
    return;
  }

  const event = JSON.parse(req.body.toString("utf8"));
  if (event.type !== "message.received") {
    res.status(202).end();
    return;
  }

  await fetch(n8nWebhookUrl, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Internal-Token": internalToken
    },
    body: JSON.stringify(event)
  });

  res.status(200).end();
});

app.listen(3000);

このサービスは WhatsApp アカウントの資格情報を保存せず、Agent の返信内容も決めません。役割は、イベントが自分の UnifyPort endpoint から届いたことを証明し、信頼できるイベントを n8n に渡すことだけです。

n8n workflow を作る

n8n で active な workflow を作成し、Webhook trigger の production URL でイベントを受け取ります。入力 JSON はすでに UnifyPort の標準イベントです。

{
  "id": "evt_2f9c1a4b7e",
  "type": "message.received",
  "provider": "whatsapp",
  "account_id": "acc_8c21d0",
  "occurred_at": "2026-07-08T02:30:00Z",
  "data": {
    "conversation": { "id": "8613912345678", "type": "user", "title": "Jordan Lee" },
    "sender": { "id": "8613912345678", "name": "Jordan Lee", "type": "user" },
    "message": {
      "id": "wamid.HBgM",
      "type": "text",
      "text": "Can I change the delivery address?",
      "direction": "inbound",
      "sent_at": "2026-07-08T02:29:59Z"
    },
    "event": { "kind": "message_received" }
  }
}

最初の実用的な workflow は 5 ノードで十分です。

  1. Webhook: 転送されたイベントを受け取る。
  2. IF: typemessage.receivedproviderwhatsapp であることを確認する。
  3. Data store、Postgres、Airtable、CRM: idaccount_idproviderdata.conversation.iddata.sender.iddata.message.text を保存する。
  4. AI Agent または model gateway への HTTP Request: sales、support、billing、人間への引き継ぎに分類する。
  5. HTTP Request: 必要な場合に UnifyPort から返信する。

先に保存してください。UnifyPort の webhook documentation では、イベントがインバウンド traffic の唯一の記録として扱われます。missed payload を後から読む message-history API はないため、遅い AI ステップが失敗する前にイベントを永続化します。

workflow が決めてから返信する

Agent が返信すべき場合は POST /v1/messages を使います。受信者は inbound event から取り、返信は明示的な action として扱います。

curl -X POST https://api.unifyport.ai/v1/messages \
  -H "X-Api-Key: <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
  "account_id": "acc_8c21d0",
  "to": { "id": "8613912345678", "type": "user" },
  "message": {
    "type": "text",
    "text": "Yes. Send us the new delivery address and we will update the order note."
  }
}'

n8n では HTTP Request node です。account_id{{$json.account_id}}、recipient id は {{$json.data.sender.id}}message.text は AI node の出力から map します。

Agent を最初の入口にしない理由

AI Agent は顧客メッセージに最初に触れるシステムにするべきではありません。最初のシステムは退屈なくらい単純であるべきです。検証、acknowledge、保存、route。そうすれば model、prompt、escalation policy が変わっても、運用上の契約は安定します。

この構造は WhatsApp 以外にも広げられます。標準 envelope は provideraccount_idoccurred_atdata を使います。あとで Telegram、LINE、Zalo、TikTok、X を接続しても、workflow は provider で分岐できます。6 種類の webhook 形式を覚える必要はありません。

公式 WhatsApp Business Developer Hub は、Meta Cloud API、webhooks、pricing、policy surface を学ぶ場所として今も重要です。公式 platform 上に構築するなら参照してください。一方で、WhatsApp inbound support agent を n8n に接続し、すべての公式セットアップ手順を workflow に持ち込みたくない場合、UnifyPort の unofficial interface は仕事を狭く定義します。署名された顧客メッセージを受け取り、チームがすでに使っているツールへ渡すことです。

まずエッジを安定させる。エッジが信頼できれば、Agent はただの次のノードになります。