Webhooks and signed event delivery
Register and store the secret
POST /v2/webhooks/subscriptions with your HTTPS URL and desired event types:
{
"url":"https://your-service.example.com/webhooks/stackai",
"event_types":["run.succeeded","run.failed"]
}Use an authorized credential with org.webhooks.manage. Store the signing secret returned at creation immediately; list responses cannot recover it. To replace a lost secret, create a replacement subscription and retire the old one carefully. There is no PATCH subscription operation in this contract.
Completion notifications and availability
Use GET /v2/runs/{run_id} as the supported way to observe a workflow's completion. Creating a webhook subscription stores its URL and selected event names; it does not enable run-event delivery. Do not replace polling with a subscription when building a run-status integration.
Create, list, and delete subscriptions
Manage the URL and accepted event types through the subscription endpoints
Observe completion of a run
Poll the run until it reaches a terminal status
Receive a run lifecycle notification
Subscription creation alone does not establish this capability; it is not a replacement for run polling
Replay a delivery or inspect delivery history
No public replay or delivery-history endpoint is provided
Accepted subscription event names are run.started, run.step.succeeded, run.step.failed, run.succeeded, run.failed, and run.cancelled. These names describe subscription filters, not a guarantee that an event will be produced for every run. The receiver guidance below applies when webhook deliveries are enabled for your deployment.
Authenticate deliveries, then deduplicate
Deliveries use the Standard Webhooks headers webhook-id, webhook-timestamp, and webhook-signature. Verify against the exact raw request body and the subscription's whsec_... secret before parsing or processing. Use a Standard Webhooks verifier with timestamp validation; JSON reserialization changes the bytes and can break verification.
Conceptual Python handler logic with the standardwebhooks package:
from standardwebhooks.webhooks import Webhook
# raw_body and headers come from your HTTP framework.
# secret comes from your secret manager, never from the incoming request.
verified_event = Webhook(secret).verify(raw_body, headers)
event_id = headers["webhook-id"]
# Atomically persist event_id and enqueue work only if it is new.
# Return 2xx after durable acceptance; process the work asynchronously.Catch verification errors and reject invalid requests. Use durable, atomic deduplication; an in-memory set is insufficient across workers or restarts. Preserve the verified event's ID with your processing record. Retries reuse the same event ID and body. Do not assume event order or exactly-once delivery.
Successful 2xx responses acknowledge delivery. Redirects are not followed. Retryable client/server failures and transport failures can be retried; the current implementation has a bounded ladder of up to ten attempts with jitter. Return a suitable transient failure when you cannot durably accept work, rather than 200 and silently losing it. A 503 with numeric Retry-After can influence retry timing within server limits.
GET subscriptions to inventory active endpoints; DELETE a subscription to stop deliveries and remove it from that list. There is no public delivery-history or replay endpoint in this contract.
Last updated
Was this helpful?

