Skip to main content
Webhooks deliver run terminals, proposals, monitor diffs, and balance warnings to your backend. They are signed per the Standard Webhooks spec — any standard-webhooks library verifies them with no custom code.

Endpoints

Manage endpoints in the dashboard or via the API:
The 201 response carries the endpoint’s whsec_… secret — this once. Store it; it cannot be read back. Alternatively, pass a per-run webhook_url on session/extraction create. Ad-hoc URLs sign with your account default secret — provisioned lazily on first use, readable and rotatable in the dashboard’s webhook settings (the one secret that isn’t shown-once, because ad-hoc URLs have no creation moment to show it at). Payload versioning: each endpoint pins the fn-version current at its creation; payloads serialize under that pin whatever your account pin does later. PATCH the endpoint to advance it deliberately. Ad-hoc webhook_url deliveries use the account pin. Delivery targets are SSRF-checked as a hard wall: https only, hosts must resolve to public unicast addresses — re-resolved and re-checked at every delivery attempt — redirects are not followed, and private/loopback/link-local/metadata ranges are refused with invalid_param.

Delivery contract

Every delivery carries:
  • Your endpoint must return 2xx within 10 seconds. Do the work after you respond.
  • Retries at 1 m, 5 m, 15 m, 1 h, 6 h, 24 h — then dead-lettered, visible in the attempts log.
  • At-least-once: dedupe on webhook-id.
  • Every attempt is kept and queryable: GET /v1/webhooks/{id}/attempts.
  • POST …/attempts/{msg}/redeliver (not yet available — planned) will redeliver a specific message by hand.
  • POST /v1/webhooks/{id}/test (not yet available — planned) will send a sample delivery of each subscribed event type on demand — signed, retried, and logged like a real one, with "test": true in the envelope, working on fn_test_ keys. Until it ships, verify your delivery path by registering an endpoint and watching the attempts log on a real (or test-key replayed) run terminal.

Verifying

Verify against the raw request body (before any JSON parsing middleware touches it); reject timestamps older than 5 minutes.

Payload envelope

Payloads are notifications, not deliveries: they carry summaries and ids — fetch rows and results via the API (they can exceed webhook size budgets). The type catalog is an open enum: dedupe on id, branch on type, ignore unknown types.

Event catalog

session.answered

session.abstained · session.error · session.stopped

The money statement rides along: what settled (backed by signed receipts) and what was released back to your balance. stopped additionally carries stop (spend-budget | query-budget | stopped).

session.proposal

A follow-up turn needs confirmation — see Budgets and proposals.

extraction.delivered · extraction.failed

monitor.changed · monitor.unchanged · monitor.failed

unchanged deliveries are opt-in per endpoint (default: change-only). failed carries reason and consecutive_failures; after 3 consecutive failures the monitor pauses itself and sends a final monitor.failed with "paused": true.

balance.low

Fires when the balance crosses your account’s threshold — before the 402, not after.

Local development

There is no first-party tunnel tool, and delivery targets must be public https (the SSRF wall refuses localhost). Use a tunnel of your choice (cloudflared, ngrok) to receive real deliveries locally. POST /v1/webhooks/{id}/test for CI is planned but not yet available — until it ships, drive a test-key replay run to a terminal and verify your handler against the real delivery it produces.