Webhooks
Webhook endpoints: where events are delivered and how they are signed. How-to: Receive webhooks.
Generated from /openapi/v1.json; interactive version: API reference.
Endpoints
Section titled “Endpoints”| method | path | what it does | success |
|---|---|---|---|
GET | /v1/webhooks | List webhook endpoints | 200 |
POST | /v1/webhooks | Register a webhook endpoint | 201 |
GET | /v1/webhooks/{id} | Retrieve a webhook endpoint | 200 |
PATCH | /v1/webhooks/{id} | Update a webhook endpoint | 200 |
DELETE | /v1/webhooks/{id} | Delete a webhook endpoint | 204 |
POST | /v1/webhooks/{id}/rotate-secret | Change a webhook endpoint’s signing secret | 200 |
POST | /v1/webhooks/{id}/test | Send a test event | 200 |
GET | /v1/webhooks/{id}/deliveries | List an endpoint’s deliveries | 200 |
GET | /v1/webhooks/{id}/deliveries/{delivery_id} | Retrieve a delivery | 200 |
GET /v1/webhooks
Section titled “GET /v1/webhooks”List webhook endpoints.
List the workspace’s webhook endpoints, newest first, in pages (has_more, next_cursor; pass it back as after). secret is never included. Owners and admins only: a key whose owner has the Agent role in the workspace gets 403 insufficient_role.
Parameters
| name | in | type | description |
|---|---|---|---|
limit | query | integer 1–200 | Max endpoints to return (1–200, default 100). Out of range → 400. |
after | query | string | The previous page’s next_cursor. |
Answers 200 with WebhookEndpointList. Errors use the error envelope; common codes: webhooks, every request.
POST /v1/webhooks
Section titled “POST /v1/webhooks”Register a webhook endpoint.
Subscribe a public https URL of at most 2,048 characters to a set of workspace event types. The response includes a one-time secret used to verify delivery signatures (Standard Webhooks / svix scheme) — it is never returned again. Deliveries are signed. A delivery is attempted at once; one not answered 2xx within 10 s is retried on the Standard Webhooks schedule (after 5 s, 5 min, 30 min, 2 h, 5 h, 10 h and 10 h: 8 attempts over about 27.6 h), then given up; an endpoint with 20 given-up deliveries in a row is disabled (re-enable with PATCH { enabled: true }). Webhooks can arrive out of order: order by the envelope seq, dedupe by id. A new endpoint receives v2 payloads (the WorkspaceEventV2 schemas) unless api_version is 2026-10-01. Owners and admins only: a key whose owner has the Agent role in the workspace gets 403 insufficient_role.
Request body: CreateWebhookRequest.
Answers 201 with WebhookEndpoint. Errors use the error envelope; common codes: webhooks, every request.
GET /v1/webhooks/{id}
Section titled “GET /v1/webhooks/{id}”Retrieve a webhook endpoint.
Owners and admins only: a key whose owner has the Agent role in the workspace gets 403 insufficient_role.
Parameters
| name | in | type | description |
|---|---|---|---|
id | path | string, required | The endpoint short id (whe-…) or UUID. |
Answers 200 with WebhookEndpointRead. Errors use the error envelope; common codes: webhooks, every request.
PATCH /v1/webhooks/{id}
Section titled “PATCH /v1/webhooks/{id}”Update a webhook endpoint.
Change the url, event_types, description or api_version, or toggle enabled. Re-enabling clears the failure state. A new api_version applies to deliveries not yet attempted. Owners and admins only: a key whose owner has the Agent role in the workspace gets 403 insufficient_role.
Parameters
| name | in | type | description |
|---|---|---|---|
id | path | string, required | The endpoint short id (whe-…) or UUID. |
Request body: UpdateWebhookRequest.
Answers 200 with WebhookEndpointRead. Errors use the error envelope; common codes: webhooks, every request.
DELETE /v1/webhooks/{id}
Section titled “DELETE /v1/webhooks/{id}”Delete a webhook endpoint.
Owners and admins only: a key whose owner has the Agent role in the workspace gets 403 insufficient_role.
Parameters
| name | in | type | description |
|---|---|---|---|
id | path | string, required | The endpoint short id (whe-…) or UUID. |
Answers 204 with no body. Errors use the error envelope; common codes: webhooks, every request.
POST /v1/webhooks/{id}/rotate-secret
Section titled “POST /v1/webhooks/{id}/rotate-secret”Change a webhook endpoint’s signing secret.
Replaces the signing secret and returns the new one once. The old secret stops working at once: every later delivery attempt, retries included, is signed with the new one, so a receiver that still checks the old secret rejects them (and they count as failed deliveries) until it is updated. Owners and admins only: a key whose owner has the Agent role in the workspace gets 403 insufficient_role.
Parameters
| name | in | type | description |
|---|---|---|---|
id | path | string, required | The endpoint short id (whe-…) or UUID. |
Answers 200 with WebhookEndpoint. Errors use the error envelope; common codes: webhooks, every request.
POST /v1/webhooks/{id}/test
Section titled “POST /v1/webhooks/{id}/test”Send a test event.
POSTs one sample webhook.test event to the endpoint, synchronously, signed with its current secret like a real delivery (10 s timeout, no redirects), and returns what the receiver answered. Works on a disabled endpoint too. Nothing is stored: no event in /v1/events, no delivery, no change to the failure count. At most 10 tests per workspace per minute (429 webhook_test_rate_limited). Owners and admins only: a key whose owner has the Agent role in the workspace gets 403 insufficient_role.
Parameters
| name | in | type | description |
|---|---|---|---|
id | path | string, required | The endpoint short id (whe-…) or UUID. |
Answers 200 with WebhookTestResult. Errors use the error envelope; common codes: webhooks, every request.
GET /v1/webhooks/{id}/deliveries
Section titled “GET /v1/webhooks/{id}/deliveries”List an endpoint’s deliveries.
The endpoint’s deliveries of the last 7 days, newest first, in pages (has_more, next_cursor; pass it back as after, with the same status). Each one says whether it succeeded, is retrying or was given up, how often it was tried, and what the receiver answered (response_status, error). The reply body and the request body are on GET /v1/webhooks/{id}/deliveries/{delivery_id}. Nothing is re-sent: replay an event from GET /v1/events?after=<seq - 1> (with &api_version=v2 for the v2 body). Owners and admins only: a key whose owner has the Agent role in the workspace gets 403 insufficient_role.
Parameters
| name | in | type | description |
|---|---|---|---|
id | path | string, required | The endpoint short id (whe-…) or UUID. |
limit | query | integer 1–100 | Max deliveries to return (1–100, default 50). Out of range → 400. |
after | query | string | The previous page’s next_cursor. A cursor of another endpoint or another status → 400 invalid_cursor. |
status | query | stringall, pending, succeeded, failed, exhausted, problems; default all | Only deliveries in this state. problems is failed and exhausted together, without the deliveries dropped because the endpoint was off or the event expired (error.code endpoint_disabled / retention). Anything else → 400. |
Answers 200 with WebhookDeliveryList. Errors use the error envelope; common codes: webhooks, every request.
GET /v1/webhooks/{id}/deliveries/{delivery_id}
Section titled “GET /v1/webhooks/{id}/deliveries/{delivery_id}”Retrieve a delivery.
One delivery, with the exact body that was (or will be) POSTed (payload), the headers of the latest try without the signature, and the first 1,000 characters of the receiver’s reply on a failed try (response_excerpt). An endpoint of another workspace, a delivery of another endpoint, and a missing one are the same 404 not_found. Owners and admins only: a key whose owner has the Agent role in the workspace gets 403 insufficient_role.
Parameters
| name | in | type | description |
|---|---|---|---|
id | path | string, required | The endpoint short id (whe-…) or UUID. |
delivery_id | path | string (uuid), required | The delivery’s id. |
Answers 200 with WebhookDeliveryDetail. Errors use the error envelope; common codes: webhooks, every request.
Objects
Section titled “Objects”WebhookEndpoint
Section titled “WebhookEndpoint”| field | type | description |
|---|---|---|
id | string, required | Short id, e.g. “whe-0000000001”. |
object | string, required always webhook_endpoint | |
url | string (uri), required | |
event_types | array of string, required | Subscribed types, or ["*"] for all. |
enabled | boolean, required | |
api_version | string, required2026-10-01, v2 | The payload version this endpoint receives: v2 (WorkspaceEventV2, the default for a new endpoint) or 2026-10-01 (WorkspaceEvent, the legacy format every endpoint that existed before release v1.84.0.0 keeps). A change applies to deliveries not yet attempted; a delivery in retry keeps the version it was first sent in. |
created_at | string (date-time), required | When the endpoint was created (ISO 8601). |
updated_at | string (date-time), required | When the endpoint was last changed (ISO 8601). |
description | string or null | |
secret | string | Signing secret (“whsec_…”). Returned ONLY by the create response and by POST /v1/webhooks/{id}/rotate-secret — never on reads. |
Example: see Receive webhooks.
WebhookEndpointRead
Section titled “WebhookEndpointRead”A webhook endpoint as list, read and update answer it: never with its secret (only create and rotate-secret return that).
| field | type | description |
|---|---|---|
id | string, required | Short id, e.g. “whe-0000000001”. |
object | string, required always webhook_endpoint | |
url | string (uri), required | |
event_types | array of string, required | Subscribed types, or ["*"] for all. |
enabled | boolean, required | |
api_version | string, required2026-10-01, v2 | The payload version this endpoint receives: v2 (WorkspaceEventV2, the default for a new endpoint) or 2026-10-01 (WorkspaceEvent, the legacy format every endpoint that existed before release v1.84.0.0 keeps). A change applies to deliveries not yet attempted; a delivery in retry keeps the version it was first sent in. |
created_at | string (date-time), required | When the endpoint was created (ISO 8601). |
updated_at | string (date-time), required | When the endpoint was last changed (ISO 8601). |
description | string or null |
Example: see Receive webhooks.
WebhookEndpointList
Section titled “WebhookEndpointList”| field | type | description |
|---|---|---|
object | string, required always list | |
data | array of WebhookEndpointRead, required | |
has_more | boolean, required | |
next_cursor | string or null, required | Pass as after for the next page; null on the last. |
Example: see Receive webhooks.
CreateWebhookRequest
Section titled “CreateWebhookRequest”| field | type | description |
|---|---|---|
url | string (uri), required | Public https endpoint. Rejected (400) if it resolves to a private/loopback/link-local/metadata address. |
event_types | array of string each conversation.created, turn.completed, confirmation.requested, confirmation.resolved, spend.recorded, run.completed, message.received, message.sent, message.status_updated, campaign.started, campaign.paused, campaign.resumed, campaign.completed, campaign.canceled, campaign.waiting, campaign.window_opened, meta_response.completed, meta_chat.updated, call.ringing, call.answered, call.completed, call.failed, * | Optional; defaults to ["*"] (all events). |
description | string ≤ 500 chars | |
api_version | string2026-10-01, v2; default v2 | The payload version the endpoint receives: v2 (default) or 2026-10-01 (legacy). See /platform-api/events/#payload-versions. |
{ "url": "https://example.com/file", "event_types": [ "conversation.created" ], "api_version": "v2"}UpdateWebhookRequest
Section titled “UpdateWebhookRequest”| field | type | description |
|---|---|---|
url | string (uri) | |
event_types | array of string each conversation.created, turn.completed, confirmation.requested, confirmation.resolved, spend.recorded, run.completed, message.received, message.sent, message.status_updated, campaign.started, campaign.paused, campaign.resumed, campaign.completed, campaign.canceled, campaign.waiting, campaign.window_opened, meta_response.completed, meta_chat.updated, call.ringing, call.answered, call.completed, call.failed, * | |
enabled | boolean | |
description | string ≤ 500 chars | |
api_version | string2026-10-01, v2 | Switch the payload version: v2 or 2026-10-01. Left out, the endpoint keeps the one it has. See /platform-api/events/#payload-versions. |
{ "event_types": [ "conversation.created" ], "api_version": "v2"}WebhookTestResult
Section titled “WebhookTestResult”| field | type | description |
|---|---|---|
object | string, required always webhook_test | |
ok | boolean, required | The receiver answered 2xx. |
status | integer or null, required | The HTTP status, or null with no answer. |
status_text | string or null, required | The status phrase, e.g. “OK”. |
duration_ms | integer, required | |
error | string or null, requiredtimeout, connect, redirect | Why no answer came: timeout (none within 10 s), connect, or redirect (redirects are not followed). Null when the receiver answered. |
response_excerpt | string or null, required | The first 1,000 characters of the reply body, when there was one. |
{ "object": "webhook_test", "ok": false, "status": null, "status_text": null, "duration_ms": 1, "error": "timeout", "response_excerpt": null}WebhookDelivery
Section titled “WebhookDelivery”One event’s delivery to one endpoint. Deliveries are kept for 7 days. The list leaves out the reply and the body: read one delivery for those.
| field | type | description |
|---|---|---|
id | string (uuid), required | The delivery’s id. |
object | string, required always webhook_delivery | |
endpoint_id | string, required | The endpoint’s short id, e.g. “whe-0000000001”. |
event_id | string, required | The event’s id (evt_…), also sent as the webhook-id header. |
event_type | string or null, required | The event’s type; null once the event left the 7-day log. |
seq | integer, required | The event’s position in the workspace’s log. Replay from this event with GET /v1/events?after=<seq - 1>. |
status | string, requiredpending, succeeded, failed, exhausted | pending (not tried yet), succeeded (answered 2xx), failed (will be retried at next_attempt_at), exhausted (given up, or dropped: see error.code). |
attempts | integer, required | Tries made so far (at most 8). |
next_attempt_at | string (date-time) or null, required | When the next retry is due; set only when status is failed. |
response_status | integer or null, required | The HTTP status of the latest try, or null when there was no answer. |
error | object or null, required | Why the latest try failed, or why the delivery was dropped; null when it did not. |
error.code | string, requiredendpoint_disabled, retention, timeout, redirect, connect, http, render | http (answered, not 2xx), timeout (no answer within 10 s), connect (no connection), redirect (redirects are not followed), render (the body could not be built; nothing was sent), endpoint_disabled (dropped: the endpoint was off), retention (dropped: the event expired before it could be delivered). |
error.message | string, required | The stored text, e.g. “HTTP 502”. |
api_version | string, required2026-10-01, v2 | The payload version this delivery was sent in (a retry resends it), or, not tried yet, will be sent in. |
created_at | string (date-time), required | |
updated_at | string (date-time), required | |
last_attempt_at | string (date-time) or null, required | When the latest try was signed (its webhook-timestamp); null before the first. |
Example: see Receive webhooks.
WebhookDeliveryDetail
Section titled “WebhookDeliveryDetail”One delivery, with the body sent and the receiver’s reply.
| field | type | description |
|---|---|---|
id | string (uuid), required | The delivery’s id. |
object | string, required always webhook_delivery | |
endpoint_id | string, required | The endpoint’s short id, e.g. “whe-0000000001”. |
event_id | string, required | The event’s id (evt_…), also sent as the webhook-id header. |
event_type | string or null, required | The event’s type; null once the event left the 7-day log. |
seq | integer, required | The event’s position in the workspace’s log. Replay from this event with GET /v1/events?after=<seq - 1>. |
status | string, requiredpending, succeeded, failed, exhausted | pending (not tried yet), succeeded (answered 2xx), failed (will be retried at next_attempt_at), exhausted (given up, or dropped: see error.code). |
attempts | integer, required | Tries made so far (at most 8). |
next_attempt_at | string (date-time) or null, required | When the next retry is due; set only when status is failed. |
response_status | integer or null, required | The HTTP status of the latest try, or null when there was no answer. |
error | object or null, required | Why the latest try failed, or why the delivery was dropped; null when it did not. |
error.code | string, requiredendpoint_disabled, retention, timeout, redirect, connect, http, render | http (answered, not 2xx), timeout (no answer within 10 s), connect (no connection), redirect (redirects are not followed), render (the body could not be built; nothing was sent), endpoint_disabled (dropped: the endpoint was off), retention (dropped: the event expired before it could be delivered). |
error.message | string, required | The stored text, e.g. “HTTP 502”. |
api_version | string, required2026-10-01, v2 | The payload version this delivery was sent in (a retry resends it), or, not tried yet, will be sent in. |
created_at | string (date-time), required | |
updated_at | string (date-time), required | |
last_attempt_at | string (date-time) or null, required | When the latest try was signed (its webhook-timestamp); null before the first. |
response_excerpt | string or null, required | The first 1,000 characters of the receiver’s reply on a failed try. |
payload | map of string to any or null, required | The exact JSON body that was (or will be) POSTed, in api_version’s format. Null once the event left the 7-day log, or when the body could not be built. |
headers | object, required | The headers of the latest try. webhook-signature is never returned. |
headers.webhook-id | string, required | |
headers.webhook-timestamp | string or null, required | Unix seconds, as Standard Webhooks defines it; null before the first try. |
Example: see Receive webhooks.
WebhookDeliveryList
Section titled “WebhookDeliveryList”| field | type | description |
|---|---|---|
object | string, required always list | |
data | array of WebhookDelivery, required | |
has_more | boolean, required | |
next_cursor | string or null, required | Pass as after for the next page; null on the last. |
Example: see Receive webhooks.