配信の動作
標準ペイロード、5秒の制限時間、再試行が行われない理由、各配信ステータスの意味を説明します。
Webhook の配信は意図的にシンプルに設計されていますが、その設計は受信側の実装に影響します。HTTP リクエストは 1 回のみ、制限時間は 5 秒、再試行はありません。ハンドラーで同期的に実行する処理の範囲を決める前に、このページをお読みください。
標準ペイロード
listing.submission 以外のすべてのイベントは、トップレベルの構造が共通で、イベント固有のフィールドは data 内に格納されます。
{ "event": "connection.location_connected", "timestamp": "2026-08-25T14:32:10Z", "account_id": 11073, "location_id": "279381", "data": { "platform": "google", "connected_account_id": "ba03bc4b-9f8c-4d3b-8e93-9628034c63cc" }}data 内の省略可能なフィールドは、空の場合、null として送信されるのではなく省略されます。想定外の入力にも対応できるように、キーが存在しない場合と明示的な null を同じように扱い、省略可能と記載されているキーが必ず存在するとは考えないでください。
2 つの例外
5 秒の制限時間
各配信は 1 回の POST で行われ、接続タイムアウトは 5 秒、読み取りタイムアウトは 5 秒です。エンドポイントが制限時間内に応答しない場合、その試行は中止され、timeout として記録されます。
この制限時間には、ハンドラーの処理全体が含まれます。安全に処理するには、次の設計にする必要があります。
- 署名を検証します。
- ペイロードをキュー、テーブル、ログなどの永続的な保存先に書き込みます。
200を返します。
それ以外のすべての処理(データの補完、複数の処理先への振り分け、REST API の呼び出し、サードパーティーにアクセスするあらゆる処理)は、キューから取り出した後に実行します。
再試行はありません
配信は必ず 1 回だけ試行されます。バックオフも、再試行キューも、デッドレターキューもありません。エンドポイントが 2 分間停止した場合、その 2 分間に発生したイベントは失われます。後から届くことはありません。
この仕様による影響として、次の 2 点を明確にしておきます。
- リダイレクトは追跡されません。リダイレクトは完全に無効化されているため、エンドポイントが
301または302を返すと、別の URL への追跡は行われず、配信失敗として記録されます。Webhook の URL には最終的な URL を設定してください。多くの Web サーバーがデフォルトで追加する、ベアドメインからwwwへのリダイレクトや、末尾のスラッシュに関するリダイレクトに注意してください。 2xxの場合のみ配信済みと見なされます。3xx、4xx、5xxはすべて失敗として記録されます。
重複と順序
- 同じイベントを2回受信することも、異なる2つの変更で同じidを受信することもあります。
data内の識別子(インタラクションid、投稿項目id、接続済みアカウントid)は、変更ではなくリソースを識別します。レビューが変更されるたびに、interaction.reviewは同じインタラクションidで再度発火し、どのペイロードにもバージョンやupdated-atフィールドが含まれないため、id +eventを基準にイベントを恒久的に除外するハンドラーは、実際の編集を通知なく破棄してしまいます。代わりに、完全に一致する重複を検出してください。ボディの生バイト列のハッシュを計算し、短い有限の時間枠(無期限ではなく数分間)内に同じハッシュが再度現れた場合に無視します。同じidでもボディが異なる場合は、重複ではなく新しい変更です。その後、REST APIからリソースを再取得し、その取得結果を正しい状態として扱ってください。書き込みをスキップせず、リソースidをキーにして書き込んでください。 - 順序は保証されず、
timestampを使っても解決しません。イベントは複数の独立した経路から発生し、到着した順に配信されるため、先に届くと想定していたlocal_post.createdよりもlocal_post.publishedが先に届くことがあります。同じリソースに対する2つの変更を、timestampで並べ替えないでください。これは変更が発生した時点ではなく配信が作成された時点で付与され、ペイロードのどこにもバージョンやシーケンス番号がないためです。順序が重要な場合は、リソースを再取得し、APIが返す内容を使用してください。 profile.updatedにはデバウンス処理が適用され、拠点ごとに60秒間に1回、時間枠の開始時に配信されます。これは遅延ではなく、実際にイベントを間引く処理です。最初の編集によって時間枠が開始され、その編集が配信されますが、時間枠内の編集はすべて破棄されて届かず、changed_fieldsには時間枠を開始した編集だけが記述されます。そのため、イベントが届くのは時間枠の開始時であり、到着をきっかけに再取得しても、その最初の編集しか確認できません。時間枠が終了した後に2回目の再取得を行うよう、イベントから少なくとも60秒後にスケジュールし、タイマーによる拠点の照合も行ってください。通知自体が失われることもあるためです。配信の試行は1回のみで、再試行はありません。それ以外にはデバウンス処理は適用されません。
イベントを受信できなかった場合の復旧
再試行も再配信もないため、どのイベント種別にも、代わりに正しい状態を取得できる読み取り手段が必要です。異常が疑われるときだけでなく、定期的に照合してください。
配信結果
外部への送信に至らなかった場合も含め、すべての配信試行にステータスが記録されます。以下は、実際に使用される文字列です。
受信側のチェックリスト
- すべてのリクエストで署名を検証し、一致しない場合は
401を返して、何も処理しないでください。 - 生のリクエストボディのバイト列をハッシュ化し、再シリアライズしたオブジェクトは決して使用しないでください。
- 処理はキューに入れ、5秒の制限時間に十分な余裕を持って
2xxを返してください。 - ハンドラーによる各書き込みでは
data内のIDをキーにして、繰り返し実行されても同じ結果になるようにしてください。ただし、同じIDを再度受信したからといって、処理が不要だと判断しないでください。 - 到着順から順序を推測せず、
timestampによる並べ替えも行わないでください。 - 受信時に
location_idを文字列に正規化してください。 - 認識できない
eventの値は無視し、その場合も200を返してください。 - WebhookのURLではリダイレクトせず、そのURLで直接リクエストを受け付けてください。
- REST APIと定期的に照合してください。再試行による補完はありません。