双方向で 1 つの秘密鍵と 1 つの署名方式を共有するので、署名の関数は一度書けば送信にも受信にも使えます。
連携方式を選ぶ
2 つは排他ではありません。1 つの Agent が、ウェブサイトのウィジェットと自社システムの API チャネルに同時に応答できます。それぞれ別の接続で、別のキーを持ちます。
環境とドメイン
すべて 1 つのアプリケーションドメインの下にあり、パスで区別します。API チャネルの受信 Webhook は
https://console-agents.fpt.ai/webhooks/api、履歴 API は https://console-agents.fpt.ai/direct-bff/… です。Webhook のアドレスは組み立てずに、必ず Console からそのままコピーしてください。API チャネルをつなぎ、キーを受け取る
1
FPT AI Agent Platform にサインインする
Console を開き、アカウントでサインインします。
2
設定する Agent を選ぶ
自社システムが転送するメッセージに応答する Agent です。
3
チャネルタブを開く
チャネル は Agent のナビゲーションバーにあります。
4
API のタイルをクリックする
API チャネルの設定パネルが開きます。
5
チャネルの設定を入力する
項目は 2 つです。下の表を参照してください。
6
設定を保存をクリックしてチャネルを作る
チャネルタブの API タイルが 設定済み に変わります。
7
新しいキーを生成をクリックする
ダイアログを閉じる前に、ルーティングキー と 署名キー をコピーしてください。

受け取るもの
コールバック URL をテストする
入力欄の横の コールバックをテスト ボタンは、署名付きのリクエストをあなたのアドレスに送り、正しい応答を期待します。実装の仕方は下の「Webhook エンドポイントを検証する」を参照してください。このボタンは組織あたり毎分 10 回までです。秘密鍵を入れ替える
1
新しいキーを生成をクリックする
この時点から、古いキーと新しいキーの両方が受け付けられます。
2
新しいキーをすべてのサーバーに配る
送信側と受信側の両方を確認してください。
3
入れ替えを完了をクリックする
古いキーが無効になります。以後は新しいキーだけが通ります。
イベントの署名を検証する
双方向のすべてのパケットは、チャネルの秘密鍵を使った HMAC-SHA256 で署名されます。署名はX-Hub-Signature-256 ヘッダーに sha256= の接頭辞付きで入り、送信時刻を載せた X-Hub-Timestamp ヘッダーが並びます。
署名の対象は、timestamp、ドット、生の本文をつないだ文字列です。
- 送るバイト列そのものに署名してください。JSON をオブジェクトに解析してから再度文字列化すると、キーの順番が変わるだけでも署名が変わります。
- タイムスタンプは署名の中に入っているので、パケットを捕まえた攻撃者が書き換えることはできません。現在の Unix 時刻を秒で送ってください。
- サーバー時刻から前後 5 分を超えてずれたタイムスタンプは拒否されます。NTP で同期しておいてください。
- 例外はありません。署名を省くモードはなく、キーを生成していない接続にも免除はありません。
Go での署名
Node.js での署名
プラットフォームから届く署名を検証する
同じ関数を再利用し、タイミングから情報が漏れないよう定数時間の比較を使います。顧客のメッセージを受け取る
顧客が自社システムに送ったメッセージは、Webhook でプラットフォームに転送します。イベントは 2 つの部分、すなわちペイロードと、それを認証する署名からなります。テキストメッセージ
ボタンやクイックリプライからのメッセージ
添付付きのメッセージ
パラメータ
postback は別の文脈として Agent に届き、顧客の発言として扱われることはありません。人が打った言葉と、画面が運んできた値を区別できるようにするためです。ボタンもクイックリプライも、このフィールド 1 つで扱います。cURL の例
レスポンスコード
200 は 受理 であって、回答済みではありません。Agent は非同期で動き、回答はそのあと Webhook に届きます。
重複排除
messageId は端から端までの重複排除キーです。同じ (integrationKey, messageId) の組み合わせを 5 分以内に再送しても認識して読み飛ばすので、タイムアウト後の再送で顧客に 2 回答えてしまうことはありません。
受信の添付
attachments の各要素には、プラットフォームが取得できる url が必要です。ファイルの中身は組織のストレージにコピーされて Agent に渡されるので、渡した URL をそのあと長く生かしておく必要はありません。
添付のアップロードは、管理者が有効にするまでオフです。オフのとき、また上限を超えたファイルや取得に失敗したファイルは読み飛ばされますが、
text は Agent に届きます。つまり添付付きのメッセージがエラーになることはなく、ファイルが取り込まれたかどうかはレスポンスコードからは分かりません。追加データ
metadata のオブジェクトは、client_metadata の下に入れ子になって Agent に届きます。トップレベルに統合されることはありません。そこにあるキー、とくに宛先の識別子が、回答の届け先を決めるからです。
本文のトップレベルにある未知のフィールドは無視されるので、独自の項目を足しても拒否されません。
Agent からのコールバック
Agent の回答は、署名付きの POST で自社の Webhook に送られます。受信方向と同じく、イベントはペイロードとその署名からなります。Webhook エンドポイントを検証する
最初の本番メッセージの前に、あなたのエンドポイントが想定どおりのものか、プラットフォームに確かめさせられます。Console の コールバックをテスト ボタンが、署名付きのリクエストを送ります。challenge をそのまま返す JSON 本文とともに 2xx を返します。
- 検証のリクエストもほかのパケットとまったく同じように署名されるので、すでに書いた関数がそのまま使えます。
typeは検証のリクエストにだけ現れ、実際のメッセージには付きません。challengeを返すことは必須です。素の 200 では足りません。放置されたドメイン、CDN のエラーページ、ロードバランサーのどれもが 200 を返しうるからです。- 検証ごとに
challengeは変わります。 - プラットフォームはリダイレクトをたどりません。301、302、303 は本文を失うので、正しく返しようがありません。
- 何も保存されません。合格は、その時点でエンドポイントが正しく答えたという意味です。
コールバックイベントの構造
命名について: 外側のフィールドは lowerCamelCase (
eventId、integrationKey、conversationId、occurredAt)、内容ブロックとその中のフィールドは snake_case (quick_replies、sub_title、image_url、file_name) です。これは手落ちではなく意図的で、内容ブロックはチャットウィジェットや会話履歴と語彙を共有しているためです。Button
QuickReply
Carousel
ボタン付きの画像は、要素 1 つのカルーセルとして送られます。
Reference
Attachment
いまのところ API チャネルの回答ターンにファイルを添付する機能はないので、
attachments がコールバックに現れたことはありません。将来の互換性のために予約されています。受信方向の添付は通常どおり動きます。ペイロードの例
ボタンのないテキストメッセージコールバックの扱い方
textはbuttonsやcarouselsがある場合も必ず入っています。表示できないときの代わりになるので、テキストしか扱えないシステムでも会話は成立します。- 空のリストは
[]ではなく、項目ごと省かれます。buttons、quick_replies、carousels、referencesは、そのターンに無ければ現れません。 conversationIdとfromは、古いバージョンのプラットフォームからのコールバックでは無いことがあります。無い場合は未確定として扱ってください。- 重複排除は
runIdではなくeventIdで行います。 - 自社データとの突き合わせも
runIdではなくconversationIdで行ってください。runIdは回答 1 ターンにすぎません。 - すぐ 200 を返し、処理は非同期にしてください。1 回あたり 10 秒しかなく、処理が遅いと失敗とみなされて再送されます。
エンドポイントが失敗したとき
コールバック URL の要件
httpsであること(http は社内開発でのみ使えます)。- URL に認証情報を埋め込まないこと。プラットフォームの認証は署名で行い、パスに隠した秘密では行いません。
- 公開インターネットのアドレスに解決されること。ループバック、プライベート、リンクローカル、ユニークローカル、マルチキャスト、キャリア NAT の各範囲は拒否されます。
- 判定は接続時に行われるので、内部アドレスに解決されるドメインも拒否されます。
- 2048 文字まで。
会話履歴を読み直す
これは、受け取れなかった回答ターンを回収する手段です。コールバックは最大 1 回の配信なので、3 回失敗したターンはエラーキューに残ります。この API がなければ、そのメッセージはあなたにとって失われたままです。
200 のレスポンス:
- チャネルの秘密鍵ではなく、組織の API キーを使います。秘密鍵は「自社サーバーがメッセージを転送している」ことの証明で、こちらは「自分のデータを自分で読む」操作だからです。
visitorTokenは自分のセッションしか読めず、ほかを読むと 403SESSION_FORBIDDENになります。 - カーソル方式のページングで、古い順に並びます。
afterに手元の最後のメッセージ ID を渡し、hasMoreで次のページの有無を見ます。 - メッセージ ID は JavaScript の安全な整数の範囲を超えるため文字列ですが、数値として並び替えられます。
roleではなくfromを読んでください。人の担当者のターンは Agent と同じroleで保存されます。fromの値はcustomer、bot、operatorの 3 つです。operatorはまだ現れません。このチャネルで担当者が Agent の代わりに応答する機能はないためです。将来の互換性のために予約されています。- 返る行はすべて実際のターンです。
text、ボタン、クイックリプライ、カルーセル、出典、添付のいずれかを持つ行だけが現れるので、空の吹き出しを除く処理は要りません。 - 不正な
afterは黙って無視されず、400 が返ります。 - 1 つの 404 が 3 つの場合をまとめています。会話が存在しない、別の組織のもの、社内の会話。意図的に区別できないようにしています。
- 添付には名前、種類、サイズだけが入り、オブジェクトキーは入りません。
- 履歴の読み出しは実行回数の枠を消費しません。既定で API キーあたり毎分 120 回という独自の枠を持ちます。
- 履歴の枠を超えると、429 に
Retry-Afterヘッダーだけが付いて返ります。
API キーと利用枠
API キーを作る
組織の API キーは、履歴 API とファイルのダウンロードリンク API で使います。Console の API キー のページで作成します。- キーは
sk-に続く 32 文字のランダムな文字列です。たとえばsk-9Kd2xQ…。 - キーは作成時に一度だけ表示されます。システムはハッシュしか保存しないので、あとから見る方法はありません。
- 一覧には先頭 12 文字が出るので、見分けられます。
- キーは作った人の権限で動くため、新しい権限が増えることはありません。
キーの失効と入れ替え
失効は削除ではなくフラグです。失効したキーの ID もあとから解決できるので、監査証跡が読める状態を保てます。一覧には失効したキーも並びます。 API キーの入れ替えは、この順番の 2 手順です。先に新しいキーを作り、次に古いキーを失効させる。その間は両方が有効です。利用状況を確認する
runsは組織の 1 日あたりの枠(UTC)で、すべての会話とチャネルで共有します。resetsAt、つまり UTC の 00:00 に戻ります。rate.limitは呼び出し元ごとの毎分の上限です。- 利用状況の確認は枠を消費しないので、何度でも呼べます。
- API キー専用です。
visitorTokenは 403USAGE_FORBIDDENで拒否されます。
次に読むもの
Live Chat チャネル
自前で作らず、用意されたチャット画面を使う場合に。
モバイル SDK
Android と iOS のアプリにチャット画面を組み込みます。
技術付録
エラーコード、システムの上限、本番前チェックリスト。