Webhook
実行のイベントを読む
GET /api/public/v1/events?pageToken= で実行のイベントをページごとに読み取ります。1 ページに完全なイベントが最大 100 件、nextPageToken が null になるまで続けます。エラーとそのまま使えるループも紹介します。
実行 Webhook には、件数と、イベント名ごとの firstPageToken が入ります。イベント本体は API から 100 件ずつ届きます。このページでは、リクエスト、レスポンス、エラー、完全なループを紹介します。
- リクエスト
GET /api/public/v1/events?pageToken=…- 認証
- 同じ組織の API キーを
x-api-keyヘッダーで送ります - ページサイズ
- 最大 100 件。
sequenceの古い順 - 次のページ
nextPageToken。最後のページではnull- トークンの有効期間
- POST の送信から 30 日
- 安定性
- Webhook のフィルターが変わらない限り、同じトークンは同じページを返します
リクエスト
1curl "$SIGNALS_HOST/api/public/v1/events?pageToken=$TOKEN" \2 -H "x-api-key: $SIGNALS_API_KEY"
SIGNALS_HOST は、サインインしているアプリのアドレスです。HTTP クライアントが自動でエンコードしない場合は、トークンを URL エンコードしてください。
pageToken があるとき、他のクエリパラメーター(cursor、limit、since、projectId、trackerId、source)は無視されます。トークンが、実行、イベント名、ページ、Webhook をすでに指定しているためです。各ページには、読み取った時点の Webhook のフィルターが適用されます。フィルターが変わっていなければ、ページには POST で数えたイベントだけが入ります。POST の後でフィルターを編集すると、ページは新しいフィルターに従うため、total、totalPages、イベントが POST の件数と異なることがあります。
レスポンス
1{2 "run": {3 "id": "5d2f7a14-9c3b-4e68-8f21-b7c4a0e19d36"4 },5 "eventName": "jobs.new",6 "total": 1,7 "page": 1,8 "totalPages": 1,9 "items": [10 {11 "version": 1,12 "eventId": "3f1c9a52-6b0e-4c3a-9d41-0a7d2b8e5f10",13 "eventType": "jobs",14 "subEventType": "job_posting",15 "action": "new",16 "signalType": "jobs.new",17 "source": "job_postings",18 "orgId": "org_3KikADUjsyXrUJ4RJhWpwcp98Yf",19 "trackerId": "b13a7c58-02d4-4e9f-8a61-5f3c9e0d2b74",20 "projectId": "2738158a-08fd-4fc4-912d-43d4c328bc10",21 "runId": "5d2f7a14-9c3b-4e68-8f21-b7c4a0e19d36",22 "competitor": {23 "name": "Acme",24 "domain": "acme.com",25 "subjectKey": "jobs:acme.com"26 },27 "occurredAt": "2026-10-27T09:12:00.000Z",28 "detectedAt": "2026-10-28T06:03:11.482Z",29 "sequence": 301,30 "itemKey": "job_postings-7421035",31 "relevance": {32 "status": "unjudged"33 },34 "jobResponse": {35 "title": "Senior Product Marketing Manager",36 "text": "Acme is hiring a Senior Product Marketing Manager to own positioning and launches for our analytics platform. You will work with product, sales and customer success to bring new features to market.",37 "url": "https://boards.greenhouse.io/acme/jobs/7421035",38 "author": null,39 "media": [],40 "metrics": null,41 "status": "open",42 "jobId": "7421035",43 "atsType": "greenhouse",44 "boardUrl": "https://boards.greenhouse.io/acme",45 "department": "Marketing",46 "team": "Product Marketing",47 "category": "marketing",48 "locations": [49 {50 "raw": "San Francisco, CA",51 "countryCode": "US"52 }53 ],54 "workplaceType": "hybrid",55 "employmentType": "full_time",56 "postedAt": "2026-10-27T09:12:00.000Z",57 "applyUrl": "https://boards.greenhouse.io/acme/jobs/7421035",58 "salary": {59 "min": 170000,60 "max": 210000,61 "currency": "USD",62 "period": "year",63 "raw": "$170,000 - $210,000"64 },65 "changes": []66 },67 "metadata": {68 "title": "Senior Product Marketing Manager",69 "text": "Acme is hiring a Senior Product Marketing Manager to own positioning and launches for our analytics platform. You will work with product, sales and customer success to bring new features to market.",70 "url": "https://boards.greenhouse.io/acme/jobs/7421035",71 "author": null,72 "media": [],73 "metrics": null,74 "status": "open",75 "jobId": "7421035",76 "atsType": "greenhouse",77 "boardUrl": "https://boards.greenhouse.io/acme",78 "department": "Marketing",79 "team": "Product Marketing",80 "category": "marketing",81 "locations": [82 {83 "raw": "San Francisco, CA",84 "countryCode": "US"85 }86 ],87 "workplaceType": "hybrid",88 "employmentType": "full_time",89 "postedAt": "2026-10-27T09:12:00.000Z",90 "applyUrl": "https://boards.greenhouse.io/acme/jobs/7421035",91 "salary": {92 "min": 170000,93 "max": 210000,94 "currency": "USD",95 "period": "year",96 "raw": "$170,000 - $210,000"97 },98 "changes": []99 }100 }101 ],102 "nextPageToken": null103}
run{ id: string }。Webhook の実行です。eventNamejobs.new。1 ページには 1 つの名前だけが入ります。totaltotal と同じ値です。page1 から totalPages。totalPagestotal を 100 で割って切り上げた値。フィルターが POST 以降に変わっていなければ、POST の totalPages と同じ値です。itemsnextPageTokenpageToken に渡します。最後のページでは null。1 つの名前のイベントが 101 件ある実行は 2 ページです(100 件と 1 件)。5,000 件なら 50 ページです。
エラー
エラーは { "error": "..." } の形です。
| ステータス | 意味 | 対処 |
|---|---|---|
400 | トークンが無効です。変更された、途中で切れた、または Signals のものではありません。 | Signals が送ったトークンをそのまま使ってください。 |
401 | API キーがない、無効、または取り消し済みです。 | x-api-key を確認してください。 |
403 | キーが Signals の組織に属していません。 | Webhook を持つ組織のキーを使ってください。 |
404 | 実行がキーの組織にない、Webhook が削除された、または実行のプロジェクトが削除されました。 | 同じ組織のキーを使ってください。削除した Webhook と削除したプロジェクトの実行はスキップしてください。 |
410 | トークンの期限切れです(30 日を過ぎています)。 | Pull API で実行を読み取ってください。実行の trackerId と、実行より前の since を指定して GET /api/public/v1/events?trackerId=…&since=… を呼び、runId が実行の id と一致する要素だけを残します。要素は signal.v1 の形です。再配信では解決しません。再配信できるのは停止した配信と失敗した配信だけで、配信履歴は 30 日間しか残りません。 |
429 | 1 つの IP から 1 分あたり 30 リクエストを超えました。 | Retry-After(60 秒)だけ待ってから、同じトークンで再度送ってください。 |
ページは何度でも取得できます
トークンは状態を持ちません。ワーカーが途中で止まったら、同じトークンで同じページを読み直してください。(Webhook のフィルターが変わっていなければ)同じイベントが返り、eventId のチェックで保存済みのものは除かれます。
ループ
エンドポイントはボディを保存して 200 を返します。その後、バックグラウンドジョブがすべてのセクションを処理します。
重複した POST をスキップする
webhookId を処理済みなら終了します。イベント名ごとに処理する
events の各エントリのうち、firstPageToken が null でないものを、そのトークンから始めます。ページを取得する
GET /api/public/v1/events?pageToken=…。429 なら Retry-After だけ待ち、同じトークンで再試行します。各イベントを保存する
eventId はスキップします。nextPageToken をたどる
nextPageToken が null になるまで繰り返します。最後に webhookId を処理済みにします。保存先の eventId に一意インデックスを付けてください。すでに保存したイベントはスキップされます。
テスト配信
テストイベントを送信は、Signals-Test: true 付きで、reviews.new が 1 件の実行 Webhook を POST します。そのトークンはサンプルの G2 レビューを返すので、実際の実行が届く前にループ全体を試せます。