Skip to main content
A webhook endpoint is a URL of yours that receives some of a project’s events as signed POST requests. Each request follows Standard Webhooks, so any of its libraries can check it.

Create an endpoint

The answer carries the endpoint’s signing secret, whsec_ and the base64 of random bytes. Store it now: it is shown on create and on rotate only, and a retry with the same idempotency key answers secret: null. Send a secret of your own (whsec_ and the base64 of 24 to 64 bytes) to keep one you already have. Endpoints and deliveries take a secret key, as runs do. On the console’s address, /webhook_endpoints and /webhook_deliveries with ?projectId= serve the project’s admins and developers, signed in or through OAuth.

Paths

Under https://<slug>.<project-domain>/workflows/v1: Every write takes an Idempotency-Key header. A retry with the same key and body never repeats the write: it answers the first result again, or 409 conflict when the first attempt was made but its answer was lost (a 5xx or a dropped connection). Read the endpoint or the deliveries to see what it did. The SDK names them client.webhooks.endpoints and client.webhooks.deliveries.

Endpoint URLs

The URL must be https, at most 2,048 characters, with no user, password or fragment, and name a public host: an IP address, localhost and cloud metadata hosts are refused. A refused URL is 422 webhook_url_invalid, with reason naming the rule: Each address the host resolves to is checked again when a request is sent, so a name that later points at a private network is not reached.

Events

An endpoint receives only the event types it lists: Each request’s body is { "type", "timestamp", "data" }, with the event’s own data. A body over 64 KiB is sent with data: { "eventId", "subjectId", "truncated": true } instead; read the event from its own API. Order is not guaranteed: use timestamp to order events, and webhook-id, the same on every retry and redelivery, to drop a repeat.

Check a request

Three headers sign each request: Check the raw body, before any JSON parsing:
Answer any 2xx within 15 seconds to take it; do slow work after answering.

Retries

A request that times out, cannot connect, or answers 408, 429 or 5xx is tried again: immediately, then after 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours and 10 hours, each wait spread by up to a fifth either way. A Retry-After header on 429 or 503 is honoured, up to an hour. A redirect or any other 4xx stops the delivery (dead), and 410 Gone also turns the endpoint off. When every attempt is used the delivery is exhausted and the project emits webhooks/delivery.exhausted. An endpoint whose every attempt has failed for 5 days is turned off with disabledReason: "failing" and webhooks/endpoint.disabled is emitted; turn it on again with PATCH { "enabled": true }. When your receiver answers 429, 502 or 504, the endpoint is slowed: every endpoint answer then carries throttleDelayMs, the pause in milliseconds, and throttledUntil, when it ends. The endpoint is slowed while throttleDelayMs is above 0, even when throttledUntil has passed. Both are left out when the endpoint is not slowed. A delivery is pending, failed (waiting for its next attempt), succeeded, exhausted or dead. Redeliver any of them; one being sent at that moment is 409 conflict.

Rotate a secret

POST /webhook_endpoints/{id}/rotate_secret answers a new secret once. The old one keeps signing beside it for 24 hours, so your receiver can move over; send overlapHours from 0 to 168 to change that, 0 to stop the old secret at once. A retry with the same Idempotency-Key does not rotate again, so the secret your receiver holds keeps signing until the overlap ends. If that retry is 409 conflict, the rotation’s new secret is not shown again: rotating once more gives you one, but stops your receiver’s secret at once.

Send from a workflow

webhook.send(id, input) in a workflow’s handler is a step: it sends once, and the handler running again for later steps sends nothing more. It takes an endpoint or a URL, never both:
type is custom when left out; otherwise words of letters, digits and _ joined by ., at most 256 characters. The endpoint receives it whatever event types it lists. Its webhook-id is the delivery’s id, it is retried, throttled and counted toward the endpoint’s health like any delivery, and it shows in the delivery log with the event type custom. To an endpoint that is turned off the delivery is dead. The step resolves once the send is stored, before the request goes out. An endpoint id the project does not have, a type on a URL send, a bad type, data that is not JSON, or a body over 64 KiB is never sent: the run fails with error.code step_failed and the reason as its message.

MCP tools

Both MCP servers offer these tools, as they do the workflows tools: the project server lists the write tools only while MCP writes are on for the project’s workflows. None takes or shows a signing secret: read a new endpoint’s secret by rotating it through the API. Errors are listed in the REST reference.