Skip to main content
A payment clears, an issue moves, a subscription lapses. The Engine verifies the provider’s signature, classifies the event, queues it durably, and delivers it to the application that subscribed — so your team writes the response rather than the retry loop. This takes two pieces that are deliberately separate:
  1. A kind: webhook config registers the inbound ingress — it makes Fused accept the delivery.
  2. An SDK attaches to it and lists which events it wants — that is what makes the event arrive.
Registering alone routes nothing.
Delivery is an SDK surface. A kind: mcp config cannot select webhooks at all — neither webhooks nor webhooks_select_all is valid on an MCP service.

Register the ingress

A kind: webhook config is a named, team-owned bundle that can span several services, with its own plan and apply lifecycle.
Apply prints each registration’s URL, which is what you give the provider:
To look one up later without re-running apply:
Its SIGNATURE column reads set or none — never the secret itself.

About name and secret

name is this config’s identity. The pair (service, name) must be globally unique per account: a second config trying to claim a pair another config already owns is a plan-time conflict, never a silent takeover. services.<slug>.secret is the signing secret used to verify inbound deliveries. It takes a bucket reference — either `${bucket.<name>.secret.<key>}` or the shorthand `${bucket.secret.<key>}` against the default bucket. Omit it entirely for a provider that does not sign its webhooks.
The bucket segment is mandatory here, unlike SDK and MCP injections. Webhook verification has no dispatch-selected bucket to fall back on, so it cannot infer one. The reference must also be the entire field value — no surrounding text.
Removing a service from the map, or deleting the file, is an ordinary apply-time diff. There is no imperative delete command.

Attach an SDK and pick events

webhook_attachment is top-level, a sibling of name and bucket — not nested under services, because one webhook config can span services the SDK also uses. Manage the event list from the CLI if you prefer:
sdk webhook add accepts --interactive when you would rather pick from a list than remember event IDs.

Handle them in your code

The generated package ships the receiver. Your process dials out to the Engine and events stream back down that connection, so there is no public endpoint to host, no ingress to open, and no signature to verify yourself — this works unchanged on a laptop, in a private VPC, or on a serverless container.
The receiver name is yours. on takes one event or a list, and only events you registered a handler for are dispatched. Call receiver.close() on shutdown. Python also exposes SyncFusedWebhooks when your process has no event loop.

The rules that bite

That last one is checked by the Engine rather than the CLI, so a name that was never applied passes validate and fails at plan. webhooks_select_all: true takes every event a service offers. It is independent of select_all for operations — you can take all events and only some operations, or the reverse. Two registrations for the same service and event never cross-deliver: an SDK only receives from the config it attached to.

Verification headers

Where a service’s imported contract declares verification_headers, treat that list as the complete reviewed header set. Source order and spelling are preserved; Fused drops blank and case-insensitive duplicate names. Only when a signature_header list is absent does OpenAPI import infer one from required header parameters on webhook operations. Do not add guessed provider headers.

Flags

webhook plan

webhook apply

Permissions

A registration with no secret reference carries no bucket permission requirement at all.

Store the signing secret

The reference resolves at verification time. The secret must be in the bucket before then.