Webhook

Webhook

リードが見つかる、ホットになる、返信する、メッセージにリアクションする。Sam AIが返信を用意する、返信を送る、会話をあなたに引き継ぐ。その瞬間に、GemleadはあなたのサーバーへHTTPSリクエストを送ります。署名付きなので、Gemleadからの送信だと確認できます。

エンドポイントの設定

エンドポイントは、アプリのAPIから、またはWebhookエンドポイントを作成で追加します。公開HTTPSのURLと受け取りたいイベントタイプを指定し、署名の検証のために、返されるsecretを保存してください。

  • HTTPSのみ対応です。URLに認証情報を含めることはできず、プライベートアドレス、ループバックアドレス、リンクローカルアドレスも使えません。
  • 1アカウントあたり最大10個のエンドポイントを登録できます。
  • filters.persona_idsを使うと、エンドポイントが受け取るイベントを、イベントタイプに関係なく特定のペルソナのリードに絞り込めます。
  • 本番のイベントが届く前に、テストイベントを送信してサーバーの動作を確認しましょう。

WebhookはBusinessプランとEnterpriseプランで利用できます。アカウントがFreeプランかStarterプランになると、エンドポイントは残りますが、BusinessプランかEnterpriseプランに戻るまで何も送信されません。

POST/v1/webhook_endpoints
curl -X POST https://gemlead.ai/api/v1/webhook_endpoints \
  -H "Authorization: Bearer $GEMLEAD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/gemlead",
    "events": ["lead.hot", "reply.received"],
    "description": "CRM sync",
    "filters": {
      "persona_ids": ["lvWBPnytpS"]
    }
  }'

イベントタイプ

すべてのイベントは同じエンベロープを持ち、dataの中身はtypeによって変わります。

タイプ送信のタイミング
lead.createdGemleadが新しいリードを見つけてスコアリングしたとき。スコアに関係なく送信されます。リードごとに1回だけ、最初のスコアがついたときに送信され、見つけてから3日以内のリードに限られるため、古いリードを再スコアリングしても送信されません。最初のスコアが60以上なら、lead.hotも送信されます。
lead.hotリードのスコアが初めて60以上になったとき。リードごとに1回だけ送信され、再スコアリングで再び送信されることはありません。
reply.receivedキャンペーン中かどうかにかかわらず、リードから新しいメッセージが届いたとき。送信から6時間以内のメッセージについて、1件ごとに1回送信されます。リードのメッセージが一度に多数届いた場合は、最新の5件だけが送信されます。
reaction.receivedリードがあなたのメッセージに絵文字でリアクションしたとき。1つのメッセージに付いたリードの絵文字ごとに1回送信されます。
sam.suggestion.readydraftモードの会話で、Sam AIが送信用の新しい返信を用意したとき。作成から6時間以内の提案について、1件ごとに1回送信されます。
sam.reply.sentSam AIの返信があなたのXアカウントから送信されたとき。autoモードでは自動で、それ以外はアプリであなたが承認したときに送信されます。送信から6時間以内の返信について、1件ごとに1回送信されます。あなたが自分で書いたメッセージでは送信されず、会話を取得で確認できます。
sam.handoffSam AIが会話をあなたに引き継いだとき。あなたなしでは適切な返信ができないと判断した場合、リードが写真かボイスメッセージだけを送ってきた場合、返信を送信できなかった場合に起こります。autoモードでは、その後のリードのメッセージへの対応はあなたに任されます。発生から6時間以内の引き継ぎについて、1件ごとに1回送信されます。

イベントオブジェクト

  • idstring
    イベントの一意の識別子。evt_に続く24文字です。Gemlead-Event-Idヘッダーにも含まれます。
  • objectstring
    常にeventです。
  • typestring
    イベントタイプ。
    lead.createdlead.hotreply.receivedreaction.receivedsam.suggestion.readysam.reply.sentsam.handoff
  • created_atstring
    イベントが発生した日時。ISO 8601形式です。
  • account_idstring
    イベントが発生したGemleadアカウント。
  • dataobject
    イベントの内容。タイプによって異なります。

lead.created

Gemleadが新しいリードを見つけてスコアリングしたとき。スコアに関係なく送信されます。リードごとに1回だけ、最初のスコアがついたときに送信され、見つけてから3日以内のリードに限られるため、古いリードを再スコアリングしても送信されません。最初のスコアが60以上なら、lead.hotも送信されます。

データ

lead.created
{
  "id": "evt_8kQ2vXr4LmT9wPz1Hc6YbN3d",
  "object": "event",
  "type": "lead.created",
  "created_at": "2026-11-05T14:03:40.117Z",
  "account_id": "a9PzK2vRtM",
  "data": {
    "object": {
      "id": "k3VnQ8wZpR",
      "object": "lead",
      "profile": {
        "x_user_id": "1592047713",
        "username": "mark_picks",
        "name": "Mark Ellis",
        "bio": "Free NBA picks every day. Sports fan first.",
        "followers_count": 2150,
        "following_count": 940,
        "location": "Denver, CO",
        "website": "https://markpicks.net",
        "profile_image_url": "https://pbs.twimg.com/profile_images/1592047713/mark_400x400.jpg",
        "verified": false,
        "url": "https://x.com/mark_picks"
      },
      "score": 38,
      "score_reason": "Shares free picks for fun and sells nothing yet, no sign of a paying community.",
      "hot": false,
      "status": "scored",
      "persona": {
        "id": "lvWBPnytpS",
        "name": "Tipsters"
      },
      "campaign": null,
      "conversation_id": null,
      "locked_to": null,
      "found_at": "2026-11-05T14:02:11.482Z",
      "scored_at": "2026-11-05T14:03:40.117Z",
      "updated_at": "2026-11-05T14:03:40.117Z",
      "metadata": {}
    }
  }
}

lead.hot

リードのスコアが初めて60以上になったとき。リードごとに1回だけ送信され、再スコアリングで再び送信されることはありません。

データ

lead.hot
{
  "id": "evt_8kQ2vXr4LmT9wPz1Hc6YbN3d",
  "object": "event",
  "type": "lead.hot",
  "created_at": "2026-11-05T14:03:40.117Z",
  "account_id": "a9PzK2vRtM",
  "data": {
    "object": {
      "id": "msgOtJ8dvT",
      "object": "lead",
      "profile": {
        "x_user_id": "1460283925",
        "username": "jane_builds",
        "name": "Jane Doe",
        "bio": "Runs a paid trading community. Daily setups, no hype.",
        "followers_count": 12400,
        "following_count": 380,
        "location": "Austin, TX",
        "website": "https://janedoe.com",
        "profile_image_url": "https://pbs.twimg.com/profile_images/1460283925/jane_400x400.jpg",
        "verified": true,
        "url": "https://x.com/jane_builds"
      },
      "score": 86,
      "score_reason": "Runs a paid trading community and asks how to collect payments for it.",
      "hot": true,
      "status": "scored",
      "persona": {
        "id": "lvWBPnytpS",
        "name": "Tipsters"
      },
      "campaign": null,
      "conversation_id": null,
      "locked_to": null,
      "found_at": "2026-11-05T14:02:11.482Z",
      "scored_at": "2026-11-05T14:03:40.117Z",
      "updated_at": "2026-11-05T14:03:40.117Z",
      "metadata": {}
    }
  }
}

reply.received

キャンペーン中かどうかにかかわらず、リードから新しいメッセージが届いたとき。送信から6時間以内のメッセージについて、1件ごとに1回送信されます。リードのメッセージが一度に多数届いた場合は、最新の5件だけが送信されます。

データ

  • leadobject
    メッセージを送ったリード。
  • messageobject
    directionがinboundの、新しいメッセージ。
  • campaignobjectまたはnull
    リードが参加しているキャンペーン。id、name、statusを含みます。
  • conversation_idstring
    スレッド全体を取得するには、会話を取得で使います。
reply.received
{
  "id": "evt_8kQ2vXr4LmT9wPz1Hc6YbN3d",
  "object": "event",
  "type": "reply.received",
  "created_at": "2026-11-05T14:03:40.117Z",
  "account_id": "a9PzK2vRtM",
  "data": {
    "lead": {
      "id": "msgOtJ8dvT",
      "object": "lead",
      "profile": {
        "x_user_id": "1460283925",
        "username": "jane_builds",
        "name": "Jane Doe",
        "bio": "Runs a paid trading community. Daily setups, no hype.",
        "followers_count": 12400,
        "following_count": 380,
        "location": "Austin, TX",
        "website": "https://janedoe.com",
        "profile_image_url": "https://pbs.twimg.com/profile_images/1460283925/jane_400x400.jpg",
        "verified": true,
        "url": "https://x.com/jane_builds"
      },
      "score": 86,
      "score_reason": "Runs a paid trading community and asks how to collect payments for it.",
      "hot": true,
      "status": "in_conversation",
      "persona": {
        "id": "lvWBPnytpS",
        "name": "Tipsters"
      },
      "campaign": {
        "id": "c7RkQ2wLxe",
        "name": "Tipsters warm intro",
        "status": "in_progress"
      },
      "conversation_id": "msgOtJ8dvT",
      "locked_to": null,
      "found_at": "2026-11-05T14:02:11.482Z",
      "scored_at": "2026-11-05T14:03:40.117Z",
      "updated_at": "2026-11-05T14:03:40.117Z",
      "metadata": {}
    },
    "message": {
      "id": "1985321409876543210",
      "object": "message",
      "direction": "inbound",
      "text": "Yes, I want to charge for my Discord. How does it work?",
      "created_at": "2026-11-06T08:12:44.000Z",
      "media": [],
      "reactions": [],
      "encrypted": false,
      "reply_to_message_id": null
    },
    "campaign": {
      "id": "c7RkQ2wLxe",
      "name": "Tipsters warm intro",
      "status": "in_progress"
    },
    "conversation_id": "msgOtJ8dvT"
  }
}

reaction.received

リードがあなたのメッセージに絵文字でリアクションしたとき。1つのメッセージに付いたリードの絵文字ごとに1回送信されます。

データ

  • leadobject
    リアクションしたリード。
  • messageobject
    リードがリアクションした、あなたのメッセージ。directionはoutboundです。
  • reactionobject
    新しいリアクション。directionはinboundです。
reaction.received
{
  "id": "evt_8kQ2vXr4LmT9wPz1Hc6YbN3d",
  "object": "event",
  "type": "reaction.received",
  "created_at": "2026-11-05T14:03:40.117Z",
  "account_id": "a9PzK2vRtM",
  "data": {
    "lead": {
      "id": "msgOtJ8dvT",
      "object": "lead",
      "profile": {
        "x_user_id": "1460283925",
        "username": "jane_builds",
        "name": "Jane Doe",
        "bio": "Runs a paid trading community. Daily setups, no hype.",
        "followers_count": 12400,
        "following_count": 380,
        "location": "Austin, TX",
        "website": "https://janedoe.com",
        "profile_image_url": "https://pbs.twimg.com/profile_images/1460283925/jane_400x400.jpg",
        "verified": true,
        "url": "https://x.com/jane_builds"
      },
      "score": 86,
      "score_reason": "Runs a paid trading community and asks how to collect payments for it.",
      "hot": true,
      "status": "in_conversation",
      "persona": {
        "id": "lvWBPnytpS",
        "name": "Tipsters"
      },
      "campaign": {
        "id": "c7RkQ2wLxe",
        "name": "Tipsters warm intro",
        "status": "in_progress"
      },
      "conversation_id": "msgOtJ8dvT",
      "locked_to": null,
      "found_at": "2026-11-05T14:02:11.482Z",
      "scored_at": "2026-11-05T14:03:40.117Z",
      "updated_at": "2026-11-05T14:03:40.117Z",
      "metadata": {}
    },
    "message": {
      "id": "1985329981234567890",
      "object": "message",
      "direction": "outbound",
      "text": "Happy to show you. Do you sell picks monthly or per event?",
      "created_at": "2026-11-06T08:20:03.000Z",
      "media": [],
      "reactions": [
        {
          "emoji": "👍",
          "direction": "inbound",
          "created_at": "2026-11-06T08:24:51.000Z"
        }
      ],
      "encrypted": false,
      "reply_to_message_id": "1985321409876543210"
    },
    "reaction": {
      "emoji": "👍",
      "direction": "inbound",
      "created_at": "2026-11-06T08:24:51.000Z"
    }
  }
}

sam.suggestion.ready

draftモードの会話で、Sam AIが送信用の新しい返信を用意したとき。作成から6時間以内の提案について、1件ごとに1回送信されます。

データ

  • leadobject
    Sam AIが返信する相手のリード。
  • conversation_idstring
    会話のSam AIを取得で使うと、提案を読み直したり、モードを変更したりできます。
  • campaignobjectまたはnull
    リードが参加しているキャンペーン。id、name、statusを含みます。
  • suggestionobject
    Sam AIが用意した返信。
sam.suggestion.ready
{
  "id": "evt_8kQ2vXr4LmT9wPz1Hc6YbN3d",
  "object": "event",
  "type": "sam.suggestion.ready",
  "created_at": "2026-11-05T14:03:40.117Z",
  "account_id": "a9PzK2vRtM",
  "data": {
    "lead": {
      "id": "msgOtJ8dvT",
      "object": "lead",
      "profile": {
        "x_user_id": "1460283925",
        "username": "jane_builds",
        "name": "Jane Doe",
        "bio": "Runs a paid trading community. Daily setups, no hype.",
        "followers_count": 12400,
        "following_count": 380,
        "location": "Austin, TX",
        "website": "https://janedoe.com",
        "profile_image_url": "https://pbs.twimg.com/profile_images/1460283925/jane_400x400.jpg",
        "verified": true,
        "url": "https://x.com/jane_builds"
      },
      "score": 86,
      "score_reason": "Runs a paid trading community and asks how to collect payments for it.",
      "hot": true,
      "status": "in_conversation",
      "persona": {
        "id": "lvWBPnytpS",
        "name": "Tipsters"
      },
      "campaign": {
        "id": "c7RkQ2wLxe",
        "name": "Tipsters warm intro",
        "status": "in_progress"
      },
      "conversation_id": "msgOtJ8dvT",
      "locked_to": null,
      "found_at": "2026-11-05T14:02:11.482Z",
      "scored_at": "2026-11-05T14:03:40.117Z",
      "updated_at": "2026-11-05T14:03:40.117Z",
      "metadata": {}
    },
    "conversation_id": "msgOtJ8dvT",
    "campaign": {
      "id": "c7RkQ2wLxe",
      "name": "Tipsters warm intro",
      "status": "in_progress"
    },
    "suggestion": {
      "messages": [
        "It takes two minutes. You set a price, members pay, and they get their Discord role right away.",
        "Want the link to set it up?"
      ],
      "stage": "answer_question",
      "why": "She asked how it works, so the reply explains it in one line and offers the link.",
      "replying_to_message_id": "1985321409876543210",
      "created_at": "2026-11-06T08:13:20.000Z"
    }
  }
}

sam.reply.sent

Sam AIの返信があなたのXアカウントから送信されたとき。autoモードでは自動で、それ以外はアプリであなたが承認したときに送信されます。送信から6時間以内の返信について、1件ごとに1回送信されます。あなたが自分で書いたメッセージでは送信されず、会話を取得で確認できます。

データ

  • leadobject
    Sam AIが返信した相手のリード。
  • conversation_idstring
    スレッド全体を取得するには、会話を取得で使います。
  • campaignobjectまたはnull
    リードが参加しているキャンペーン。id、name、statusを含みます。
  • messagesstringsの配列
    Sam AIが送信したDM。送信順です。
  • sent_bystring
    Sam AIが自動で送信した場合はauto、アプリであなたが承認した場合はapproved。
    autoapproved
  • replying_to_message_idstringまたはnull
    返信の対象になったリードのメッセージ。
  • sent_atstring
    送信された日時。ISO 8601形式です。
sam.reply.sent
{
  "id": "evt_8kQ2vXr4LmT9wPz1Hc6YbN3d",
  "object": "event",
  "type": "sam.reply.sent",
  "created_at": "2026-11-05T14:03:40.117Z",
  "account_id": "a9PzK2vRtM",
  "data": {
    "lead": {
      "id": "msgOtJ8dvT",
      "object": "lead",
      "profile": {
        "x_user_id": "1460283925",
        "username": "jane_builds",
        "name": "Jane Doe",
        "bio": "Runs a paid trading community. Daily setups, no hype.",
        "followers_count": 12400,
        "following_count": 380,
        "location": "Austin, TX",
        "website": "https://janedoe.com",
        "profile_image_url": "https://pbs.twimg.com/profile_images/1460283925/jane_400x400.jpg",
        "verified": true,
        "url": "https://x.com/jane_builds"
      },
      "score": 86,
      "score_reason": "Runs a paid trading community and asks how to collect payments for it.",
      "hot": true,
      "status": "in_conversation",
      "persona": {
        "id": "lvWBPnytpS",
        "name": "Tipsters"
      },
      "campaign": {
        "id": "c7RkQ2wLxe",
        "name": "Tipsters warm intro",
        "status": "in_progress"
      },
      "conversation_id": "msgOtJ8dvT",
      "locked_to": null,
      "found_at": "2026-11-05T14:02:11.482Z",
      "scored_at": "2026-11-05T14:03:40.117Z",
      "updated_at": "2026-11-05T14:03:40.117Z",
      "metadata": {}
    },
    "conversation_id": "msgOtJ8dvT",
    "campaign": {
      "id": "c7RkQ2wLxe",
      "name": "Tipsters warm intro",
      "status": "in_progress"
    },
    "messages": [
      "It takes two minutes. You set a price, members pay, and they get their Discord role right away.",
      "Want the link to set it up?"
    ],
    "sent_by": "auto",
    "replying_to_message_id": "1985321409876543210",
    "sent_at": "2026-11-06T08:14:05.000Z"
  }
}

sam.handoff

Sam AIが会話をあなたに引き継いだとき。あなたなしでは適切な返信ができないと判断した場合、リードが写真かボイスメッセージだけを送ってきた場合、返信を送信できなかった場合に起こります。autoモードでは、その後のリードのメッセージへの対応はあなたに任されます。発生から6時間以内の引き継ぎについて、1件ごとに1回送信されます。

データ

  • leadobject
    返信が必要なリード。
  • conversation_idstring
    スレッド全体を取得するには、会話を取得で使います。
  • campaignobjectまたはnull
    リードが参加しているキャンペーン。id、name、statusを含みます。
  • reasonstringまたはnull
    Sam AIが会話を引き継いだ理由。Sam AIが書いたものです。
sam.handoff
{
  "id": "evt_8kQ2vXr4LmT9wPz1Hc6YbN3d",
  "object": "event",
  "type": "sam.handoff",
  "created_at": "2026-11-05T14:03:40.117Z",
  "account_id": "a9PzK2vRtM",
  "data": {
    "lead": {
      "id": "msgOtJ8dvT",
      "object": "lead",
      "profile": {
        "x_user_id": "1460283925",
        "username": "jane_builds",
        "name": "Jane Doe",
        "bio": "Runs a paid trading community. Daily setups, no hype.",
        "followers_count": 12400,
        "following_count": 380,
        "location": "Austin, TX",
        "website": "https://janedoe.com",
        "profile_image_url": "https://pbs.twimg.com/profile_images/1460283925/jane_400x400.jpg",
        "verified": true,
        "url": "https://x.com/jane_builds"
      },
      "score": 86,
      "score_reason": "Runs a paid trading community and asks how to collect payments for it.",
      "hot": true,
      "status": "in_conversation",
      "persona": {
        "id": "lvWBPnytpS",
        "name": "Tipsters"
      },
      "campaign": {
        "id": "c7RkQ2wLxe",
        "name": "Tipsters warm intro",
        "status": "in_progress"
      },
      "conversation_id": "msgOtJ8dvT",
      "locked_to": null,
      "found_at": "2026-11-05T14:02:11.482Z",
      "scored_at": "2026-11-05T14:03:40.117Z",
      "updated_at": "2026-11-05T14:03:40.117Z",
      "metadata": {}
    },
    "conversation_id": "msgOtJ8dvT",
    "campaign": {
      "id": "c7RkQ2wLxe",
      "name": "Tipsters warm intro",
      "status": "in_progress"
    },
    "reason": "She wants a call before she signs up."
  }
}

配信

Gemleadは各イベントをJSONのPOSTで送信し、10秒間レスポンスを待ちます。2xxであれば配信成功です。リダイレクト、そのほかのステータス、タイムアウト、接続エラーは失敗とみなされ、イベントは再試行されます。

ヘッダー値
Gemlead-Signatureタイムスタンプと署名。t=1762444800,v1=5f2b...の形式です。
Gemlead-Event-Idイベントのid。処理済みのイベントをスキップするために使います。
Gemlead-Event-Typeイベントのtype。
User-AgentGemlead-Webhooks/1.0
リクエスト
POST /webhooks/gemlead HTTP/1.1
Content-Type: application/json; charset=utf-8
User-Agent: Gemlead-Webhooks/1.0
Gemlead-Event-Id: evt_8kQ2vXr4LmT9wPz1Hc6YbN3d
Gemlead-Event-Type: lead.hot
Gemlead-Signature: t=1762444800,v1=58ca5e964b92d617c979d3f067cdc2035a1a1a9d3f848d7d927c2db09b8e9baa

署名の検証

あなたのURLには誰でもリクエストを送れます。信頼する前に、各リクエストがGemleadからのものか確認してください。

  1. JSONとしてパースする前に、生のボディをバイト列として読み取ります。パースしてから書き戻すと、バイト列が変わってしまいます。
  2. Gemlead-Signatureヘッダーをカンマで分割します。tは秒単位のUnix時刻、v1は署名です。
  3. t、ドット、生のボディをつなげた値のHMAC SHA256を、whsec_を含むエンドポイントのシークレット全体をキーにして計算し、16進数で表します。
  4. その値を各v1と、定数時間の比較で照合します。
  5. 古いリクエストを使い回されないよう、サーバーの時計と5分以上ずれたtは拒否します。再試行のたびに、新しいtで署名されます。

検証に失敗した場合は400を返してください。シークレットはGEMLEAD_WEBHOOK_SECRETのような環境変数に保存します。

const crypto = require("crypto");
const express = require("express");

// rawBody: the request body exactly as received, before any JSON parsing.
function verifyGemleadSignature(rawBody, header, secret, toleranceSeconds = 300) {
  if (typeof header !== "string") return false;
  let timestamp = null;
  const signatures = [];
  for (const part of header.split(",")) {
    const [key, value] = part.split("=", 2).map((s) => (s || "").trim());
    if (key === "t") timestamp = value;
    if (key === "v1" && value) signatures.push(value);
  }
  if (!/^\d+$/.test(timestamp || "") || signatures.length === 0) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > toleranceSeconds) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.`).update(rawBody).digest();
  return signatures.some((signature) => {
    const given = Buffer.from(signature, "hex");
    return given.length === expected.length && crypto.timingSafeEqual(given, expected);
  });
}

const app = express();

// The body must stay raw on this route.
app.post("/webhooks/gemlead", express.raw({ type: "application/json" }), (req, res) => {
  const ok = verifyGemleadSignature(
    req.body,
    req.get("Gemlead-Signature"),
    process.env.GEMLEAD_WEBHOOK_SECRET,
  );
  if (!ok) return res.status(400).send("Invalid signature");

  const event = JSON.parse(req.body.toString("utf8"));
  // Already handled? Skip it, retries send the same event.id again.
  if (event.type === "lead.created") {
    // event.data.object is the new lead, hot or not
  } else if (event.type === "lead.hot") {
    // event.data.object is the lead
  } else if (event.type === "reply.received") {
    // event.data.lead, event.data.message, event.data.campaign, event.data.conversation_id
  }
  res.sendStatus(200);
});

app.listen(3000);

再試行

配信に失敗すると、Gemleadは待ち時間を少しずつ延ばしながら7回再試行します。最初の試行から約45時間後の8回目でも失敗すると、その配信は失敗として記録されます。

試行送信のタイミング
1すぐに
2前回の失敗から1分後
3前回の失敗から5分後
4前回の失敗から30分後
5前回の失敗から2時間後
6前回の失敗から6時間後
7前回の失敗から12時間後
8前回の失敗から24時間後

3日連続で失敗し、失敗した試行が8回以上になったエンドポイントは無効になります。statusはdisabledに、disabled_reasonはfailingに変わり、待機中の配信はすべて失敗として終了します。サーバーを修正したら、アプリまたはWebhookエンドポイントを更新で再び有効にしてください。カウントはそこからやり直しになります。テストイベントは一度だけ送信され、このカウントには含まれません。

配信を一覧表示では、各試行のステータスコードとエラーを確認でき、サーバーが何を返したかがわかります。

ベストプラクティス

  • 10秒以内に2xxを返し、実際の処理はキューで行ってください。
  • 配信は少なくとも1回行われます。処理済みのイベントは、Gemlead-Event-Idにも入っているイベントのidを使ってスキップしてください。
  • イベントは順不同で届くことがあります。created_atを比較するか、リードを取得し直して最新の状態を確認してください。
  • すべての署名を検証し、古いタイムスタンプは拒否してください。
  • シークレットを再発行すると、すぐに置き換わります。新しいシークレットはすぐにデプロイしてください。その間に失敗した配信は、新しいシークレットで署名されて再試行されます。
  • イベントと配信は30日間保持されます。