Webhooks
Headers, signature construction, payload schema, and retry policy for run events.
Add an HTTPS webhook URL to a routine and every finished run produces a signed event. Work on Repeat returns the signing secret once and stores only its encrypted form.
For the receiver walkthrough, see Receive run webhooks.
Headers
Content-Type: application/json
X-Work-On-Repeat-Event: run.succeeded | run.failed
X-Work-On-Repeat-Request-Id: <stable across retries>
X-Work-On-Repeat-Timestamp: <unix seconds>
X-Work-On-Repeat-Signature: v1=<hex hmac-sha256>Signature
signed_payload = timestamp + "." + raw_request_body
signature = "v1=" + hex(hmac_sha256(webhook_secret, signed_payload))Verify against the exact bytes received, compare in constant time, and reject timestamps outside a five-minute window before doing any work.
Payload
Every event shares a base:
Prop
Type
run.succeeded
{
"version": 1,
"event": "run.succeeded",
"deliveryId": "0f2e9d1c-…",
"occurredAt": "2026-08-10T09:03:41.522Z",
"projectId": "5a41c0de-…",
"routine": { "id": "9c2e…", "name": "Weekly security audit" },
"run": {
"id": "1f7b0e6a-…",
"state": "succeeded",
"trigger": "schedule",
"output": "Five findings, ranked…",
"error": null
}
}output is always a non-empty string on success; a run that produced no text
is delivered as "Routine completed without text output.".
run.failed
{
"version": 1,
"event": "run.failed",
"deliveryId": "3b81a44f-…",
"occurredAt": "2026-08-10T09:21:07.004Z",
"projectId": "5a41c0de-…",
"routine": { "id": "9c2e…", "name": "Weekly security audit" },
"run": {
"id": "77c1de90-…",
"state": "failed",
"trigger": "schedule",
"output": null,
"error": {
"category": "gateway_timeout",
"message": "The gateway did not respond in time.",
"retryable": true
}
}
}retryable is true for gateway_error, gateway_rate_limit,
gateway_timeout, mcp_transport, and timeout. It describes whether
repeating the work is reasonable — Work on Repeat does not re-execute the run
itself.
Only completed and failed runs are delivered
There are no events for queued, claimed, or running. A run that ends
cancelled or timed_out is recorded on the run, and timed_out is
delivered as run.failed with the timeout category.
Delivery behaviour
Deliveries are persisted before they are sent, then claimed and attempted by the one-minute operations tick.
| Response | Outcome |
|---|---|
2xx | Delivered. |
429, 5xx | Retried with backoff. |
| Network error or timeout | Retried with backoff. |
Other 4xx | Exhausted immediately; not retried. |
| Redirect | Not followed. |
Four attempts maximum. Backoff starts at 30 seconds and doubles, capped at 30 minutes, with jitter. Request timeout is 10 seconds.
Each attempt is recorded on the run with its attempt number and response code, visible in the run detail.
Rotating the secret
Set the routine's webhook URL again. The response returns a fresh
webhookSecret once, and the previous secret stops verifying. Clearing the URL
removes the stored secret entirely.