The events
message.delta and message.block are realtime-only. Streaming fragments over HTTP would be a poor trade, and a render block is a thing a UI draws, not a thing a server acts on.
The four “agent needs something” events are webhooked. If your integration is headless, they matter more than
run.completed — a run parked at one of them waits until you answer, and nothing else will happen in the meantime.Envelope
Every event has the same shape, whichever transport carries it:sourceis the unit of work the event is about.kindisrunfor agent work,requestfor a bare product call like extract, andendpointfor a test.originis which surface produced the work:agent,api,mcp,consoleorplayground.contextcarries tenancy and routing. A key is left out rather than sent as null when it does not apply — a run whose agent uses no connection has noconnection_id.seqorders events within onesource. Product requests have none.run_idandthread_idsit at the top level for compatibility with the SDK. They duplicatesourceandcontext; new code should read the structured fields.
data varies by type. write.confirmation_requested carries the tool_call_id, patch_ref, summary and risk you need to build an approval prompt.
One event per unit of work
An event describes the outermost thing you asked for, not every step inside it. If an agent extracts three documents during a run, you get onerun.completed — not three extract.completed. The extracts are steps of that run, and the run’s own event already reports it. extract.* fires only when you call the extract API directly.
If you do want per-step visibility inside a run, subscribe to tool_call.completed and read data.name. That is a separate choice, and it is a noisy one: you will get every tool call in every run, not just the ones you had in mind.
Registering an endpoint
Under Settings → Webhooks. An endpoint is a subscription, not just a URL:- Events — exactly the types you pick. Nothing is selected for you, and types we add later never start arriving on an existing endpoint.
- Where the work came from —
agent,api,mcp,console,playground. Playground is off unless you ask for it, so test messages never reach a production server. - Which backend — one or more connections. This is how you keep local, staging and production apart; there is no separate test mode.
- Which agent — by agent, covering all its versions.
- Send ids only — omits
data, leaving you the ids to read the resource back. Keeps run content out of your ingress logs.
extract.completed. Within a category it can describe, an absent value fails a filter you set — an agent that uses no connection has no connection, so a connection filter will not match its runs.
Verifying a delivery
Every delivery carries three headers:
The signature is computed exactly as for tool calls — see Signing and verification — but keyed with the webhook endpoint’s secret, not your workspace request secret. A delivery goes to the URL you registered and carries no query string, so the signed target is just the path.
Two rules that are easy to miss:
- Reject a timestamp more than five minutes old. The signature alone does not stop a captured delivery being replayed at you; the timestamp does.
- Accept if any
v2matches. A header can carry more than one. During a secret rotation we sign with both the old and the new secret for 24 hours so you do not need an atomic deploy, and a verifier that reads only the first will break halfway through.
Delivery behaviour
Respond2xx quickly and do the work asynchronously. Anything 300 or above is a failure, redirects are not followed, and we give up on the response after 8 seconds. A slow handler burns your retry budget.
Failures retry on a fixed schedule — immediately, then after 30s, 2m, 10m, 1h and 6h. Six attempts over roughly eight hours, with a little jitter so a server coming back up is not hit by everything at once.
Deliveries are not ordered, and a retry can land after a later event. Deduplicate on Patchwork-Delivery and treat each event as a statement about a run you can re-fetch, rather than as a state machine of its own. seq orders events within a single run if you need it.
An endpoint that fails continuously for 24 hours is switched off and workspace owners are emailed. Switching it off does not delete anything — the configuration, the secret and the delivery history all stay, and turning it back on does not resend the backlog.
When something is wrong
The endpoint’s page shows every delivery: the type, the state, the status we got, the attempt count, and — when you open a row — the exact payload we sent and the response we got back. That is usually enough to tell whether the problem is on your side or ours. From there you can replay a delivery, which re-sends the stored payload under the same event id, so your handler dedupes it exactly as it would a retry. Send test delivers a syntheticendpoint.test event to one endpoint. It ignores every filter and works on an endpoint that is switched off, which is when you most need it. It never counts toward the failure that disables an endpoint, and nothing can subscribe to it.
Rotating the secret
Rotating gives you a new secret immediately and keeps the old one valid for 24 hours, signing every delivery with both. Take the new secret, deploy it, and the old one stops mattering on its own — provided your verifier checks everyv2.
Only workspace owners can see or rotate a signing secret.