Webhooks API
Create webhooks, read their deliveries, and recover what a receiver missed. These endpoints are the /api/webhooks write plane of a self-hosted instance; the guide to receiving and verifying is Webhooks.
List webhooks
GET/api/webhooksSelf-hosted instancesEvery webhook on this instance, newest first, with status and last delivery times. Paged by offset.
Filters
Rows to skip. Default 0.
Range and paging
Page size, 1 to 200. Default 50.
Returns
A cursor envelope. data holds Webhook summary objects.
- 200One page of webhooks
- 401This bind requires the instance token, and it was missing or wrong
Create a webhook
POST/api/webhooksSelf-hosted instancesTakes triggers and starts at the chain tip, with no backfill. The response carries the signing secret. This is the only time it is returned, so store it now; rotate-secret issues a new one.
Body
Unique name on this instance.
Delivery target, http:// or https://. Private addresses are refused at delivery time.
1 to 50 triggers. An event matching any of them is delivered.
Payload and signing format. standard-webhooks signs with the webhook's secret; the others shape the body for that receiver.
6 possible values
standard-webhooksinngesttriggercloudflarecloudeventsrawPer-format receiver credentials: token / tokenEnc for trigger and cloudflare, headers or basicAuth for raw. Write-only — never returned; a read gets auth instead.
Attempts before an event moves to the dead-letter queue. Default 7.
Per-attempt HTTP timeout in milliseconds. Default 10000.
Deliveries in flight at once for this webhook. Default 4.
Returns
The Webhook with its signing secret object.
- 201Created. The body carries the signing secret, once
- 400A parameter, filter, cursor or body field was refused;
codesays which. A body carryingsubgraphName,tableNameorfilteris answered withSUBGRAPH_WEBHOOKS_REMOVED: subgraph webhooks were removed - 401This bind requires the instance token, and it was missing or wrong
- 409A webhook with that name already exists
- 415Missing or non-JSON
Content-Type(UNSUPPORTED_MEDIA_TYPE) — writes must sendContent-Type: application/json - 500The webhook could not be stored (
INTERNAL_ERROR)
Get a webhook
GET/api/webhooks/{id}Self-hosted instancesFull config and delivery health for one webhook. Never includes the signing secret.
Path parameters
Webhook id (UUID), as returned by create or list.
Returns
- 200The webhook
- 401This bind requires the instance token, and it was missing or wrong
- 404No webhook with this id
Update a webhook
PATCH/api/webhooks/{id}Self-hosted instancesChange any subset of fields; at least one is required. triggers are fixed at create: to change them, create a new webhook.
Path parameters
Webhook id (UUID), as returned by create or list.
Body
New unique name.
New delivery target, http:// or https://.
Payload and signing format. standard-webhooks signs with the webhook's secret; the others shape the body for that receiver.
6 possible values
standard-webhooksinngesttriggercloudflarecloudeventsrawPer-format receiver credentials: token / tokenEnc for trigger and cloudflare, headers or basicAuth for raw. Write-only — never returned; a read gets auth instead.
Attempts before an event moves to the dead-letter queue. Default 7.
Per-attempt HTTP timeout in milliseconds. Default 10000.
Deliveries in flight at once for this webhook. Default 4.
Returns
- 200The updated webhook
- 400A parameter, filter, cursor or body field was refused;
codesays which. A body carryingsubgraphName,tableNameorfilteris answered withSUBGRAPH_WEBHOOKS_REMOVED: subgraph webhooks were removed - 401This bind requires the instance token, and it was missing or wrong
- 404No webhook with this id
- 415Missing or non-JSON
Content-Type(UNSUPPORTED_MEDIA_TYPE) — writes must sendContent-Type: application/json
Delete a webhook
DELETE/api/webhooks/{id}Self-hosted instancesRemoves the webhook. Nothing more is delivered to its URL. Cannot be undone.
Path parameters
Webhook id (UUID), as returned by create or list.
Returns
- 200Deleted
- 400A parameter, filter, cursor or body field was refused;
codesays which - 401This bind requires the instance token, and it was missing or wrong
- 404No webhook with this id
- 415Missing or non-JSON
Content-Type(UNSUPPORTED_MEDIA_TYPE) — writes must sendContent-Type: application/json
Pause a webhook
POST/api/webhooks/{id}/pauseSelf-hosted instancesSets status to paused. Matched events keep queueing and deliver on resume. Takes no body.
Path parameters
Webhook id (UUID), as returned by create or list.
Returns
- 200The paused webhook
- 400A parameter, filter, cursor or body field was refused;
codesays which - 401This bind requires the instance token, and it was missing or wrong
- 404No webhook with this id
- 415Missing or non-JSON
Content-Type(UNSUPPORTED_MEDIA_TYPE) — writes must sendContent-Type: application/json
Resume a webhook
POST/api/webhooks/{id}/resumeSelf-hosted instancesSets status to active and resets the circuit breaker (circuitFailures, circuitOpenedAt). Queued events start delivering. Takes no body.
Path parameters
Webhook id (UUID), as returned by create or list.
Returns
- 200The resumed webhook
- 400A parameter, filter, cursor or body field was refused;
codesays which - 401This bind requires the instance token, and it was missing or wrong
- 404No webhook with this id
- 415Missing or non-JSON
Content-Type(UNSUPPORTED_MEDIA_TYPE) — writes must sendContent-Type: application/json
Rotate the signing secret
POST/api/webhooks/{id}/rotate-secretSelf-hosted instancesIssues a new signing secret and returns it, once. Deliveries signed with the old secret stop verifying immediately, so update your receiver right away. Takes no body.
Path parameters
Webhook id (UUID), as returned by create or list.
Returns
The Webhook with its signing secret object.
- 200The webhook and its new signing secret
- 400A parameter, filter, cursor or body field was refused;
codesays which - 401This bind requires the instance token, and it was missing or wrong
- 404No webhook with this id
- 415Missing or non-JSON
Content-Type(UNSUPPORTED_MEDIA_TYPE) — writes must sendContent-Type: application/json
Send a test delivery
POST/api/webhooks/{id}/testSelf-hosted instancesPosts one sample event, built and signed in the webhook's format, to its URL through the SSRF guard, and logs it under deliveries. The body has data.test: true, so receivers can tell it from a live event. Works while paused. Takes no body.
Path parameters
Webhook id (UUID), as returned by create or list.
Returns
The Webhook test result object.
- 200What the target answered. A failed delivery still returns 200; check
ok - 400A parameter, filter, cursor or body field was refused;
codesays which - 401This bind requires the instance token, and it was missing or wrong
- 404No webhook with this id
- 415Missing or non-JSON
Content-Type(UNSUPPORTED_MEDIA_TYPE) — writes must sendContent-Type: application/json
List delivery attempts
GET/api/webhooks/{id}/deliveriesSelf-hosted instancesThe last 100 delivery attempts, newest first, including test deliveries. total counts every attempt ever logged for the webhook. With from and to, only attempts dispatched in that window; total counts the window.
Path parameters
Webhook id (UUID), as returned by create or list.
Query parameters
Returns
A cursor envelope. data holds Webhook delivery objects.
- 200Recent delivery attempts
- 401This bind requires the instance token, and it was missing or wrong
- 404No webhook with this id
List dead-lettered events
GET/api/webhooks/{id}/deadSelf-hosted instancesEvents that used up maxRetries without a 2xx, newest failure first, up to 100. Requeue one to try it again.
Path parameters
Webhook id (UUID), as returned by create or list.
Returns
A cursor envelope. data holds Dead-lettered webhook event objects.
- 200Dead-lettered events
- 401This bind requires the instance token, and it was missing or wrong
- 404No webhook with this id
Requeue a dead event
POST/api/webhooks/{id}/dead/{outbox_id}/requeueSelf-hosted instancesMoves one dead event back to pending with its attempt count reset. It delivers at live priority, even if it came from a replay. Takes no body.
Path parameters
Returns
- 200Requeued
- 400A parameter, filter, cursor or body field was refused;
codesays which - 401This bind requires the instance token, and it was missing or wrong
- 404No webhook with this id, or no dead event with this id on it
- 415Missing or non-JSON
Content-Type(UNSUPPORTED_MEDIA_TYPE) — writes must sendContent-Type: application/json
Replay a block range
POST/api/webhooks/{id}/replaySelf-hosted instancesQueues historical events in a block range for redelivery, capped at 100,000 blocks. Replays are idempotent: the same range yields the same replayId and delivers nothing twice unless force is set. They drain through a 10% share of the outbox so live traffic keeps priority, and never move the live cursor. Replays deliver the normal chain.<type>.apply envelope. Watch one with GET /api/webhooks/{id}/replays/{replay_id}.
Path parameters
Webhook id (UUID), as returned by create or list.
Body
Returns
- 202Replay queued
- 400A parameter, filter, cursor or body field was refused;
codesays which. Also:fromBlockaftertoBlock, or a range over 100,000 blocks - 401This bind requires the instance token, and it was missing or wrong
- 404No webhook with this id
- 409A replay of this webhook is still scanning its range
- 415Missing or non-JSON
Content-Type(UNSUPPORTED_MEDIA_TYPE) — writes must sendContent-Type: application/json - 500The replay could not be queued (
INTERNAL_ERROR)
List recent replays across webhooks
GET/api/webhooks/replaysSelf-hosted instancesReplays still scanning or delivering, plus any started in the last 24 hours, across every webhook on this instance. Newest first.
Range and paging
Page size, 1 to 50. Default 20.
Returns
A cursor envelope. data holds Webhook replay progress objects.
- 200Recent replays with live progress
- 401This bind requires the instance token, and it was missing or wrong
List a webhook's replays and replay ranges
GET/api/webhooks/{id}/replaysSelf-hosted instancesThe webhook's last 10 replays with live progress, newest first, and the block ranges a new replay can start from. Ranges count back from the last block the evaluator processed.
Path parameters
Webhook id (UUID), as returned by create or list.
Returns
A cursor envelope. data holds Webhook replay progress objects.
- 200Replays and ranges for this webhook
- 401This bind requires the instance token, and it was missing or wrong
- 404No webhook with this id
Get a replay's progress
GET/api/webhooks/{id}/replays/{replay_id}Self-hosted instancesWhere one replay stands: how many events it queued, how many are done, and once nothing is left pending, how many delivered and how many gave up. Poll it while the replay drains.
Path parameters
Returns
The Webhook replay progress object.
- 200The replay's progress
- 401This bind requires the instance token, and it was missing or wrong
- 404No webhook with this id, or no replay with this id on it
Requeue dead events in bulk
POST/api/webhooks/{id}/dead/requeueSelf-hosted instancesMoves dead events back to pending with their attempt counts reset, at live priority. With no body it requeues every dead event of the webhook; replayId narrows it to one replay and since to events that failed at or after that time. A replay that gets events back returns to running, so its progress tracks the retry.
Path parameters
Webhook id (UUID), as returned by create or list.
Body
Returns
- 200Events requeued
- 400A parameter, filter, cursor or body field was refused;
codesays which - 401This bind requires the instance token, and it was missing or wrong
- 404No webhook with this id
- 415Missing or non-JSON
Content-Type(UNSUPPORTED_MEDIA_TYPE) — writes must sendContent-Type: application/json
List insights
GET/api/insightsSelf-hosted instancesRanked findings about this instance's webhooks and subgraphs: losing events first, then about to fail, then costing money, then recently recovered. Dismissed and expected insights are left out.
Query parameters
Only one resource, as <kind>:<id>, such as webhook:<id> or subgraph:<name>.
Returns
- 200Insights in rank order
- 400
resourceis not<kind>:<id>(VALIDATION_ERROR) - 401This bind requires the instance token, and it was missing or wrong
Dismiss an insight
POST/api/insights/{id}/dismissSelf-hosted instancesHides this incident. If the same rule fires again after it clears, the new incident shows. Takes no body.
Path parameters
Insight id (UUID), as returned by the insights list.
Returns
- 200Dismissed
- 400A parameter, filter, cursor or body field was refused;
codesays which - 401This bind requires the instance token, and it was missing or wrong
- 404No insight with this id
- 415Missing or non-JSON
Content-Type(UNSUPPORTED_MEDIA_TYPE) — writes must sendContent-Type: application/json
Undo dismissing an insight
DELETE/api/insights/{id}/dismissSelf-hosted instancesShows a dismissed insight again.
Path parameters
Insight id (UUID), as returned by the insights list.
Returns
- 200Shown again
- 400A parameter, filter, cursor or body field was refused;
codesays which - 401This bind requires the instance token, and it was missing or wrong
- 404No insight with this id
- 415Missing or non-JSON
Content-Type(UNSUPPORTED_MEDIA_TYPE) — writes must sendContent-Type: application/json
Snooze an insight until a time
POST/api/insights/{id}/snoozeSelf-hosted instancesHides this insight until the time you give, at most 7 days ahead. It shows again then, or earlier if you remove the snooze.
Path parameters
Insight id (UUID), as returned by the insights list.
Body
When it shows again. Must be in the next 7 days.
Returns
- 200Snoozed
- 400
untilis not a time in the next 7 days (VALIDATION_ERROR) - 401This bind requires the instance token, and it was missing or wrong
- 404No insight with this id
- 415Missing or non-JSON
Content-Type(UNSUPPORTED_MEDIA_TYPE) — writes must sendContent-Type: application/json
Remove an insight's snooze
DELETE/api/insights/{id}/snoozeSelf-hosted instancesShows a snoozed insight again now.
Path parameters
Insight id (UUID), as returned by the insights list.
Returns
- 200Shown again
- 400A parameter, filter, cursor or body field was refused;
codesays which - 401This bind requires the instance token, and it was missing or wrong
- 404No insight with this id
- 415Missing or non-JSON
Content-Type(UNSUPPORTED_MEDIA_TYPE) — writes must sendContent-Type: application/json
Mark an insight expected
POST/api/insights/{id}/expectedSelf-hosted instancesMutes this rule for this resource, including future incidents, until you remove the mute. Takes no body.
Path parameters
Insight id (UUID), as returned by the insights list.
Returns
- 200Muted
- 400A parameter, filter, cursor or body field was refused;
codesays which - 401This bind requires the instance token, and it was missing or wrong
- 404No insight with this id
- 415Missing or non-JSON
Content-Type(UNSUPPORTED_MEDIA_TYPE) — writes must sendContent-Type: application/json
Unmark an insight as expected
DELETE/api/insights/{id}/expectedSelf-hosted instancesRemoves the mute for this rule and resource, so it shows again whenever it fires.
Path parameters
Insight id (UUID), as returned by the insights list.
Returns
- 200Unmuted
- 400A parameter, filter, cursor or body field was refused;
codesays which - 401This bind requires the instance token, and it was missing or wrong
- 404No insight with this id
- 415Missing or non-JSON
Content-Type(UNSUPPORTED_MEDIA_TYPE) — writes must sendContent-Type: application/json
A webhook as it appears in the list.
Returned by List webhooks
Attributes
Webhook id.
Unique name on this instance.
active delivers. paused holds new events in the outbox until resumed; the circuit breaker also pauses a webhook after 20 straight failures. error is reserved; nothing sets it today.
3 possible values
activepausederrorAlways chain: webhooks fire on raw chain events matched by triggers.
1 possible values
chainPayload and signing format. standard-webhooks signs with the webhook's secret; the others shape the body for that receiver.
6 possible values
standard-webhooksinngesttriggercloudflarecloudeventsrawDelivery target.
Last delivery attempt, successful or not.
Last delivery the target answered with a 2xx.
When the circuit breaker paused this webhook. Cleared on resume.
Consecutive failed deliveries; a success resets it. The circuit opens at 20; resume resets it.
Product that owns this webhook's name, derived from a reserved name prefix (for example sentinel: is Sentinel). When set, renaming returns 409 WEBHOOK_NAME_MANAGED. null for ordinary webhooks.
When the webhook was created.
Last config or status change.
Returned by create and rotate-secret only. The one place the signing secret appears.
Returned by Create a webhook, Rotate the signing secret
A webhook's full config and delivery health. Never includes the signing secret.
Returned by Get a webhook, Update a webhook, Pause a webhook, Resume a webhook
Attributes
Webhook id.
Unique name on this instance.
active delivers. paused holds new events in the outbox until resumed; the circuit breaker also pauses a webhook after 20 straight failures. error is reserved; nothing sets it today.
3 possible values
activepausederrorAlways chain: webhooks fire on raw chain events matched by triggers.
1 possible values
chainPayload and signing format. standard-webhooks signs with the webhook's secret; the others shape the body for that receiver.
6 possible values
standard-webhooksinngesttriggercloudflarecloudeventsrawDelivery target.
Last delivery attempt, successful or not.
Last delivery the target answered with a 2xx.
When the circuit breaker paused this webhook. Cleared on resume.
Consecutive failed deliveries; a success resets it. The circuit opens at 20; resume resets it.
Product that owns this webhook's name, derived from a reserved name prefix (for example sentinel: is Sentinel). When set, renaming returns 409 WEBHOOK_NAME_MANAGED. null for ordinary webhooks.
When the webhook was created.
Last config or status change.
Chain triggers.
Receiver credentials, redacted — set with authConfig on create or update, but never returned. authConfig is write-only.
Attempts before an event moves to the dead-letter queue. Default 7.
Per-attempt HTTP timeout in milliseconds. Default 10000.
Deliveries in flight at once for this webhook. Default 4.
Error from the last failed delivery.
When the receiver's TLS certificate expires, from a periodic probe. null for http receivers, unreachable hosts, and webhooks not probed yet.
Set when this instance's chain-trigger evaluator isn't running (SUBGRAPH_SOURCE != streams-index) — the webhook will never fire until that's fixed. null otherwise.
Outcome of one test delivery.
Returned by Send a test delivery
Attributes
true when the target answered 2xx.
HTTP status. null when no response came back.
Why the delivery failed. null on success.
Round trip in milliseconds.
The logged attempt, as it appears under deliveries.
One HTTP attempt to deliver an event.
Returned by List delivery attempts
Attributes
Attempt id.
Position in the webhook's lifetime log. The newest attempt equals total.
Which try this was for its event, from 1.
HTTP status. null when no response came back.
Block of the delivered event. null for test deliveries and for events already compacted out of the outbox.
When the delivered event's block was produced. null for test deliveries, events already compacted out of the outbox, and rows written before this column existed.
Why the attempt failed. null on success.
Round trip in milliseconds.
Start of the target's response body.
When the attempt was sent.
An event that used up its retries without a 2xx.
Returned by List dead-lettered events
Attributes
Outbox id. Pass it as outbox_id to requeue.
Delivered type, e.g. chain.ft_transfer.apply.
Attempts made before it was dead-lettered.
Block the event came from.
Transaction behind the event, when there is one.
The event body that failed to deliver.
When the last attempt failed.
When the event was queued.
What a replay queued.
Returned by Replay a block range
Live state of one replay. While it runs, delivered and gaveUp are null; they are set once no event is left pending.
Returned by List recent replays across webhooks, List a webhook's replays and replay ranges, Get a replay's progress
Attributes
16 hex characters.
Webhook id (UUID).
scanning while the range is read, running while events deliver, done when none are pending, failed if the scan broke.
4 possible values
scanningrunningdonefailedFirst block, inclusive.
Last block, inclusive.
Events the replay queued.
Events delivered or given up on: total minus pending.
Events still waiting to be delivered or retried.
Events delivered. null until the replay is done.
Events that used up their retries. null until the replay is done.
ISO 8601 time it started.
ISO 8601 time it finished. null while it runs.
Why the scan failed. null otherwise.