SteveSignalsDocs

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 のフィルターが変わらない限り、同じトークンは同じページを返します

リクエスト

bash
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 の件数と異なることがあります。

レスポンス

GET /api/public/v1/events?pageToken=… · jobs.new(Acme)正確な形のサンプル(実データはまだありません)
json
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": null
103}
イベントのページ
run
object
{ id: string }。Webhook の実行です。
eventName
string
このページのイベント名。例: jobs.new。1 ページには 1 つの名前だけが入ります。
total
number
Webhook の現在のフィルターに一致する、この名前のイベント数。フィルターが POST 以降に変わっていなければ、POST の total と同じ値です。
page
number
このページの番号。1 から totalPages。
totalPages
number
total を 100 で割って切り上げた値。フィルターが POST 以降に変わっていなければ、POST の totalPages と同じ値です。
items
object[]
最大 100 件のイベント。sequence の順です。各要素はイベントの形(version は 1)で、それぞれ固有の eventId を持ちます。
nextPageToken
string|null
次のページの pageToken に渡します。最後のページでは null。

1 つの名前のイベントが 101 件ある実行は 2 ページです(100 件と 1 件)。5,000 件なら 50 ページです。

エラー

エラーは { "error": "..." } の形です。

ステータス意味対処
400トークンが無効です。変更された、途中で切れた、または Signals のものではありません。Signals が送ったトークンをそのまま使ってください。
401API キーがない、無効、または取り消し済みです。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 日間しか残りません。
4291 つの 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 レビューを返すので、実際の実行が届く前にループ全体を試せます。