自社のシステムからテンプレートを送る

1 回の API 呼び出しで、承認済みの WhatsApp テンプレートを 1 つの番号に送ります — webhook から、cron ジョブから、スクリプトから。キー、テンプレート ID、リクエスト、すべての拒否、そして設計時に考慮すべき 2 つの点。

更新日

人が送るのではないテンプレートもあります。タイドウォーター・ランドリーは、予約 システムが時間帯を確定した瞬間に集荷確定のメッセージを送りたいと考えています — そのために誰かが受信トレイを開く必要はないはずです。そのためのエンドポイントが 1 つあります。

POST https://api.omniocto.com/api/wa-templates/{id}/send

これは承認済みのテンプレートを 1 つの電話番号に送ります。webhook のハンドラーや スクリプトから呼び出してください — 「注文が発送されたら集荷テンプレートを送る」 のように。テンプレートなので、24 時間の窓は関係ありません (理由)。

API キーを取得する

キーは /settings/api-keys にあります — 歯車メニューの 管理とシステム → API キー です。

  1. キーを作成 をクリックします。ダイアログ API キーを作成 で 名前(任意) を求められます — 「例: CI パイプライン」 のように、使うシステムの名前を付けて ください。
  2. API キーを作成しました にキーが一度だけ表示され、「このキーは今すぐコピー してください。二度と表示できません。」 と添えられます。システムがシークレットを 保管する場所に保存してください。

一覧には各キーの 最終使用 日が表示され、キーを無効化 で即座に停止できます: 「これを使用している連携はすべて失敗します。」 連携ごとにキーを 1 つ使えば、これを 安全に行えます。

キーはすべての呼び出しで Authorization: Bearer <key> として送ります。

テンプレート ID を調べる

/templates/whatsapp を開いてテンプレートをクリックします。パネルが開き、ページの アドレスに &template=wt-… が付きます — この wt- の短い ID をパスに入れます。 (テンプレートの UUID でも動作します。)

同じパネルに、呼び出しで何を埋める必要があるかが表示されます。

  • 変数 — 本文の変数: 「2 個の位置変数」({{1}}、{{2}})か、 名前: に続く変数名のどちらかです。
  • ヘッダー — そのテキスト、またはメディアヘッダーなら [IMAGE]、[VIDEO]、 [DOCUMENT]。
  • ボタン — 順番に並び、それぞれに種類([URL]、[COPY_CODE]、 [QUICK_REPLY])が付きます。最初のボタンがインデックス 0 です。

スクリプトからは、GET /api/wa-templates?status=approved で承認済みテンプレートを shortId、name、language、category とともに一覧できます。

送信できるのは承認済みのテンプレートだけで、ほかのステータスは拒否されます。

リクエスト

ボディのフィールドは 2 つです。

フィールド内容
to電話番号。E.164 形式 — +14255550123
componentsMeta の template.components 配列そのもの。そのまま渡されます。変数のないテンプレートでは省略します

components は Meta の Cloud API に送るのと同じ配列なので、Meta のドキュメントが そのまま当てはまります。要点は次のとおりです。

  • ヘッダー — パラメーター 1 つ: 変数を含むテキストヘッダーなら text、または https:// の link を持つ image、video、document。
  • 本文 — text パラメーター。位置指定のテンプレート({{1}}、{{2}})では、 名前なしで順番どおりに送ります。名前付きのテンプレート({{customer_name}}) では、すべてのパラメーターに parameter_name を付けます。両者を混ぜないで ください。Octo が作ったテンプレートは、どんな名前を付けたかにかかわらず、ここでは 位置指定です。
  • ボタン — 値を受け取るボタンごとにコンポーネントを 1 つ。sub_type と index(テンプレートのボタン一覧での位置)を付けます:
    • url — text パラメーター 1 つ: リンクの変わる部分。
    • copy_code — コード。Meta の { "type": "coupon_code", "coupon_code": "FRESH20" } 形式、または { "type": "text" }。最大 15 文字。
    • quick_reply — 任意。タップされたときに payload を受け取りたい場合は { "type": "payload" } パラメーター。

完全な例

タイドウォーター・ランドリーの集荷テンプレート pickup_confirmation は、本文が Hi {{1}}, your pickup is confirmed for {{2}}. Reply here to change it. で、 リンクが注文番号で終わる Track pickup ボタンが 1 つあります。

curl -X POST "https://api.omniocto.com/api/wa-templates/wt-a1b2c3d4e5/send" \
  -H "Authorization: Bearer $OMNI_OCTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+14255550123",
    "components": [
      { "type": "body", "parameters": [
        { "type": "text", "text": "Ana" },
        { "type": "text", "text": "Tuesday, 9–11am" }
      ] },
      { "type": "button", "sub_type": "url", "index": "0",
        "parameters": [{ "type": "text", "text": "TW-20931" }] }
    ]
  }'

テンプレートが名前付き変数を使っていれば、本文は各値に名前を付けます。

{ "type": "body", "parameters": [
  { "type": "text", "parameter_name": "customer_name", "text": "Ana" },
  { "type": "text", "parameter_name": "pickup_slot", "text": "Tuesday, 9–11am" }
] }

成功は 201 です。

{
  "messageId": "6f1c…",
  "conversationId": "3a9e…",
  "conversationShortId": "cv-9f8e7d6c5b",
  "externalId": "wamid.HBgL…",
  "isNewConversation": false,
  "template": { "id": "…", "shortId": "wt-a1b2c3d4e5", "name": "pickup_confirmation", "language": "en_US" }
}

externalId は、Meta が配信通知と既読通知を報告するときに使う ID で、これらの通知は 後から会話に届きます。201 は Meta が送信を受け付けたという意味で、電話が受信した という意味ではありません。 conversationShortId は保存しておいてください — 後で 使います。

メッセージの届く場所

/conversations の、テンプレートの WhatsApp 番号上でその電話番号と交わしている 会話 — お客様のメッセージが届くのと同じスレッドです。

  • その番号との会話がすでにあれば、テンプレートはそこに加わります — 会話が クローズされていれば、再オープンされます。isNewConversation は false です。
  • なければ新しい会話が開かれます(未知の番号なら連絡先も作成されます)。 isNewConversation は true です。
  • 吹き出しには値が入った本文が表示され、テンプレート · とテンプレート名のタグが 付きます。

お客様が返信すると、その返信は同じスレッドに届きます — そして、API による送信で スレッドが人に引き渡されることはありません。すでにあった会話は、それまで担当して いた人(またはエージェント)がそのまま担当します。クローズされていた会話や新しい 会話は、その WhatsApp 番号に応答する AI エージェントのもとでアクティブになります。 つまり、その番号にエージェントがいれば、人が /conversations で スレッドを引き継いで いない限り、そのエージェントが返信に答えます。

送信が拒否されたとき

ステータスcode意味
400—to が E.164 形式でない、または components が Meta の受け付ける形になっていない。ボディに問題点が並びます
402billing_not_eligibleワークスペースがまだ送信メッセージを送れない
404template_not_foundこのワークスペースにその ID のテンプレートがない
409blocked_dnc番号が配信を停止している。メッセージはブロック済みとしてスレッドに残り、Meta には何も届きません
422template_not_approvedMeta がテンプレートを承認していない。status で現在の状態がわかります
422template_components_mismatchcomponents がテンプレートに合っていない。error が問題を示します
422channel_unavailableテンプレートの WhatsApp 番号が削除されたか無効になっている
422channel_pending_verification, workspace_suspended, route_blocked番号またはワークスペースに対する送信チェックが拒否した。メッセージはブロック済みまたは失敗としてスレッドに残ります
429workspace_rate_limitedワークスペースがこのエンドポイントのレートを超えている。何も書き込まれません
429rate_limitedWhatsApp 番号のバースト枠がいっぱい — 数秒から数分待ちます
429daily_limit_reached番号の 1 日のテンプレート上限を使い切った — 待ち時間は数時間になることがあります
502send_failedMeta が送信を拒否した。error に Meta のメッセージが入ります。メッセージは失敗としてスレッドに残ります

429 にはすべて Retry-After ヘッダーと、ボディに同じ値の retryAfterSec が 付きます。その時間待ってから同じ呼び出しを送り直してください。最初の呼び出しでは メッセージは送信も保存も課金もされていません。(新しい番号への最初の送信が番号の 上限で拒否された場合は、会話だけが空のまま作られています。ボディにその conversationShortId が入ります。)

2 種類の 429 は別々の上限です。ワークスペースは、デフォルトでこのエンドポイントを 毎分 300 回、毎秒最大 10 回のバーストで呼び出せます。このレートは API 呼び出しだけを 数え、Omni Octo のサポートがワークスペースごとに設定します。WhatsApp 番号には 独自の送信枠があり、受信トレイ、Octo、キャンペーンと共有されます — 1 つの番号はどれだけ速く送れるか で説明しています。

不一致なら何も送られない

呼び出しは、何かを書き込む前に components をテンプレートと照合します。値の 不足、余分な値、未知の名前、誤った種類のヘッダー、値を取らないボタンへの パラメーター — いずれも 422 template_components_mismatch で、メッセージは保存 されず、何も送られません。error が何が問題かを正確に伝えます。

body expects 2 parameters, got 1
body is missing variable "pickup_slot"
button 0 is a QUICK_REPLY button, got url
button 0 (a URL button) is missing its url parameter

つまり、不一致は直して送り直しても安全です。

呼び出しは冪等ではない

成功した呼び出しは、1 回ごとにテンプレートを 1 通送り、課金されます。冪等キーは ありません。呼び出しがタイムアウトしたり接続が切れたりすると、送られたかどうか わからず、やみくもに再試行すると 2 回送られることがあります。

タイムアウト後に再試行する前に、スレッドを確認してください —

GET /api/conversations/{conversationShortId}

その番号への以前の送信で得た ID を使うか、/conversations で会話を開きます。 テンプレートがそこになければ、そのときだけ送り直してください。

上の拒否は事情が違います。4xx は送信が行われなかったことを意味します。

このエンドポイントでできないこと

  • 自由文の送信。 送れるのは承認済みテンプレートだけです — 窓の中での返信には 受信トレイを 使ってください。
  • リストへの送信。 1 回の呼び出しは 1 つの番号です。リストは キャンペーンが扱います。
  • Meta の判断を覆すこと。 201 の後でも、マーケティング用テンプレートは Meta に 拒否されることがあります — 理由。

次に読む