Skip to main content
A webhook is an HTTPS URL you register for your organization. When a session changes status, the platform sends a signed POST request to every subscribed webhook, so you can react to completions and failures without polling. Manage webhooks with the CRUD API. Each one has a target url, a list of enabled_events, and a signing secret returned once at creation.

Events

All event types share the same payload shape. "*" subscribes to the session.status_updated firehose. Each type in enabled_events is delivered independently: subscribing to both session.status_updated and session.failed gets you two deliveries when a session fails, one per type. List the available types programmatically with List event types.

Delivery payload

Each delivery is a POST with a JSON body:
Delivery payload
type
string
The event type, e.g. session.status_updated.
id
string
Unique id for this event.
created_at
string
When the status change occurred (UTC, RFC 3339, millisecond precision).
data
object
Event payload: session_id, the new status (queued, pending, running, paused, idle, awaiting_tool_results, completed, failed, timed_out, or interrupted), and the previous_status it transitioned from (null when unknown).

Verifying deliveries

Every delivery carries these headers: Always verify before trusting a delivery. The SDKs ship a helper that checks the signature, rejects stale deliveries (older than 5 minutes by default), and parses the event.
Verify against the raw request body bytes, exactly as received. Parsing and re-serializing the JSON changes the bytes and invalidates the signature.
To verify manually: compute HMAC-SHA256(secret, "{timestamp}." + raw_body), hex-encode it, prefix with sha256=, and compare it to X-H-Webhook-Signature using a constant-time comparison. Reject deliveries whose timestamp is more than a few minutes old to guard against replays.

Delivery semantics

Deliveries are at-least-once, with a 10-second timeout per attempt. If your endpoint is unreachable or returns a non-2xx status, delivery is retried with increasing backoff, up to 8 attempts spanning about 24 hours, after which the event is dropped. Because of retries:
  • Deduplicate. The same event can arrive more than once. The event id and the X-H-Webhook-Delivery header are stable across retries; skip ids you have already processed.
  • Ignore arrival order. A retried old event can land after a newer one. Trust the event’s own status, created_at, and previous_status, not the order of arrival.
  • Return 2xx quickly. Any other status counts as a failure and schedules a retry. Do slow work after responding.
Webhooks are a trigger rather than a source of truth: on receipt, fetch the authoritative state with Get session status. Each webhook records the result of its latest delivery attempt: Retrieve returns last_delivery_status, last_delivery_error, last_delivery_at, last_success_at, and consecutive_failures, so you can check whether an endpoint is healthy, and why it was disabled, without digging through receiver logs.

Testing an endpoint

Ping sends a signed ping event through the real delivery path and returns your endpoint’s HTTP response synchronously, so you can validate URL, signature verification, and connectivity before relying on the webhook.

Rotating the secret

Rotate replaces the signing secret without a verification gap:
  1. Deploy your receiver passing both the current and a placeholder for the new secret to the verify helper (it accepts a list).
  2. Call the rotate endpoint and store the new secret; update the receiver’s secret list.
  3. Once deliveries verify against the new secret, remove the old one.

Constraints

  • Target URLs must be https:// and resolve to a public address. Reachability is verified with a HEAD request when you register or change the URL, and deliveries to private or internal hosts fail (and count as failed attempts).
  • The signing secret is returned only by Create and Rotate.
  • An organization can register up to 10 webhooks.
  • A disabled webhook stays registered but receives no deliveries.
  • After 50 consecutive failed delivery attempts, a webhook is automatically disabled. Fix the receiver, then re-enable it with Update ({"disabled": false}).