Webhook Events

These are outbound webhook events: your server gets notified when runs complete. For inbound webhooks that start runs, see Webhook Triggers.

Register webhook endpoints to receive notifications when runs complete, fail, or need input. Events are delivered as signed HTTP POST requests with automatic retries.

Register a Webhook

Python

Important: The secret is only shown once, on creation. Store it securely for signature verification.

Available Events

EventDescriptionDefault
run.startedA run began execution (fires on every attempt, including replies/resumes)No
run.completedA run finished successfullyYes
run.failedA run encountered an errorYes
run.cancelledA run was cancelledYes
run.awaiting_inputA run is waiting for user input or approvalNo
connection.expiredA connected app lost authorization (token expired or revoked)No
balance.lowPrepaid token balance fell to/below your warning threshold (prepaid accounts)No
balance.criticalPrepaid balance is almost gone (20% of the threshold); runs will fail soonNo
balance.depletedPrepaid balance hit $0; runs are paused until you add creditNo

The balance.* events let you react before runs start failing (auto-top-up, page on-call). Each carries balance_micros, balance_usd, and currency; set the threshold with client.billing.set_alert_threshold(low_balance_threshold_cents=...).

Payload Format

JSON

teammate_id is the wire name for the agent id: JSON request and response bodies keep it. The SDK accepts agent_id (canonical) and teammate_id alike.

output is the agent's closing message, clean prose. On run.completed, three envelope fields ride along: when the agent needs a decision from your user, needs_reply is true (needs_reply_count says how many separate decisions are waiting) — surface the output, collect a reply, and continue the run with a follow-up message. headline is the agent's one-line outcome summary (may be null), capped at 200 characters — useful as a notification title, but it is agent-generated text: escape it like any untrusted string and never interpolate it into HTML or commands unescaped.

Treat run.failed as the latest state, not a permanent one. When the failure was a transient infrastructure error, the platform may automatically retry or resume the same run, so a run.failed can be followed by run.started and a terminal run.completed for the same run id. Key your handler on the most recent event.

For run.awaiting_input, data.status is awaiting_approval. When the pause is a question (AskUserQuestion), data.questions carries the full question list, so no separate runs.permissions() call is needed. See the payload example in Human-in-the-Loop. Tool-approval pauses omit questions.

Cancellation payload (run.cancelled)

Cancellations fire their own run.cancelled event. Until mid-2026 they arrived as run.failed with data.status: "cancelled"; webhooks created before then should add run.cancelled to their subscription.

JSON

Handling Connection Expiry

When a connected app's token expires or is revoked, a connection.expired event fires. Use it to notify the affected user and trigger a reconnect flow. App triggers that use the expired connection are disabled automatically; reconnecting re-enables the flow. See Webhook Triggers.

Payload and handling code: re-initiate OAuth on expiry

Signature Verification

Verification needs no client and no API key. Pass the raw request body: a parsed-then-restringified object will not match, because re-serializing does not preserve key order or spacing.

Each request includes Webhook-Id, Webhook-Timestamp, and Webhook-Signature headers, where the signature is v1=HMAC-SHA256(secret, "{id}.{timestamp}.{body}"). Use the SDK helper to verify:

Manage Webhooks

Python

Retry Behavior

Delivery is attempted up to 3 times with exponential backoff (2s, 4s delay); any 2xx counts as success. After all attempts the delivery is marked failed, visible via list_deliveries(). Pending deliveries are also retried if the server restarts.

Next: Webhook Triggers · Streaming · Scheduling

Was this page helpful?