Guide to Claude Webhooks: Features and Best Practices
Claude Managed Agent webhooks are HTTP callbacks that Anthropic uses to notify your application of major state changes in long-running agent sessions — when a run starts, when an agent idles waiting for input, when an outcome evaluation finishes, when a multiagent thread terminates, when a vault credential's refresh fails. They are the primary way to react to agent activity asynchronously without polling the API, and they sit alongside the inbound routines.fire endpoint that lets external systems trigger Claude Code routines in the first place.
This guide covers how Claude webhooks work, the features Anthropic provides, the limitations and pain points worth knowing about before you build, and the best practices that keep production integrations reliable.
How Claude webhooks work
When you create a webhook endpoint in the Claude Console, Anthropic generates a whsec_-prefixed signing secret and starts delivering events for the types you've subscribed to. Each delivery is an HTTP POST with a JSON body and an X-Webhook-Signature header. Your endpoint must return a 2xx status to acknowledge receipt; anything else — including 3xx redirects — is treated as a failure and retried.
| Feature | Details |
|---|---|
| Webhook configuration | Claude Console under Manage > Webhooks, or via the Anthropic API |
| Verification method | HMAC signature in X-Webhook-Signature header, whsec_-prefixed secret |
| Replay protection | Five-minute freshness window enforced by SDK helpers |
| Acknowledgement | Any 2xx response; 3xx and 4xx/5xx are failures |
| Retry logic | At-least-once delivery; same event.id reused across retries |
| Auto-disable | After ~20 consecutive failures, or immediately on private-IP / redirect |
| Ordering | Not guaranteed; sort by created_at if order matters |
| Payload shape | Thin — just event type and id; fetch full object via API |
| SDK support | Python, TypeScript, Go, Java, C#, PHP, Ruby — client.beta.webhooks.unwrap() |
Claude's design choices here are deliberate. Thin payloads avoid serving stale data on retries and keep deliveries small. At-least-once delivery prioritises reaching your endpoint over avoiding duplicates. The five-minute freshness window protects against replay attacks but means your endpoint has to acknowledge quickly. Each of these choices has a flip side, which we cover in the limitations section below.
Claude webhook features
Comprehensive session and vault event types
Claude webhooks cover two families of events. Session events track the lifecycle of agent runs: session.status_run_started, session.status_idled, session.status_rescheduled, session.status_terminated, plus the multiagent equivalents session.thread_created, session.thread_idled, session.thread_terminated, and the session.outcome_evaluation_ended for when an agent's work passes or fails its rubric. Vault events cover credential lifecycle including vault_credential.refresh_failed, which is one of the most operationally relevant events for any team running agents in production.
You subscribe per-endpoint to exactly the event types you want, plus test events. Endpoints don't receive events they didn't subscribe to.
Console-based endpoint configuration
Webhook endpoints are managed in the Claude Console under Manage > Webhooks. Each endpoint requires a public HTTPS URL (port 443, no private IPs), a list of subscribed event types, and a generated whsec_ signing secret that's shown only once at creation. The same UI shows recent deliveries, failure counts, and the auto-disable status of each endpoint.
SDK signature verification helpers
Anthropic ships SDKs in seven languages (Python, TypeScript, Go, Java, C#, PHP, and Ruby) each with a client.beta.webhooks.unwrap() helper that takes the raw request body, the signature header, and the signing secret, verifies the HMAC, enforces the five-minute freshness window, and returns a parsed event. The helper throws on invalid signatures or stale payloads, so applications get one safe entry point instead of writing verification code per language.
Thin-payload pattern with API hydration
Every delivery returns the event type, the event id, the created_at timestamp, and a data object containing the affected resource's type, id, organization_id, and workspace_id — and nothing else. To get the full session object, the latest message, the outcome verdict, or anything else specific to the resource, your handler calls back into the Anthropic API. This avoids stale-data races on retries and keeps every webhook delivery small enough to verify and queue cheaply.
Auto-disable for failing endpoints
If an endpoint accumulates roughly twenty consecutive failed deliveries, Claude automatically disables it and records a machine-readable disabled_reason. Endpoints are also disabled immediately if their hostname resolves to a private IP or if they return any redirect. Disabled endpoints have to be re-enabled manually in the Console after the underlying issue is fixed. This protects Claude's delivery infrastructure, but it also means your operational tolerance for endpoint failures is lower than you might assume.
Security considerations
Verifying webhook signatures
Every webhook delivery includes an X-Webhook-Signature header containing an HMAC computed with the endpoint's signing secret. Verification is non-optional in production: without it, your handler is an unauthenticated public endpoint that does work on receipt of any HTTP POST.
The recommended approach is the SDK helper — client.beta.webhooks.unwrap(), available in Python, TypeScript, Go, Java, C#, PHP, and Ruby — which takes the raw request body, the headers, and the signing secret, and returns a parsed event. The helper throws if the signature is invalid or if the payload is more than five minutes old. Both checks are important: signature verification alone doesn't protect against replay attacks where an attacker captures a valid signed delivery and re-sends it later.
For a code-level walk-through of both manual verification and the SDK helper, see our guide to securing and verifying Claude webhooks with Hookdeck.
Store webhook secrets securely
Treat the whsec_ secret like a database credential. Store it in environment variables or a secrets manager — never in source control, never hardcoded. Anthropic's SDKs read it from ANTHROPIC_WEBHOOK_SIGNING_KEY by default. Rotating the secret means generating a new one in the Console, deploying the new value, and revoking the old one — Claude doesn't currently support overlapping secrets, so plan rotations during low-traffic windows or route through a verification proxy that can hold both.
Claude webhook limitations and pain points
Thin payloads require API round-trips
The Problem: Webhook payloads contain only type and id. To act on an event your handler usually has to make an additional API call to hydrate the full resource — and during high-volume periods, those round-trips compound.
Why It Happens: Anthropic deliberately ships thin payloads to avoid serving stale data on retries: the resource you fetch is always the current state, not whatever was true at delivery time.
Workarounds:
- Cache hydrated resources by
idif your handler is likely to see the same session multiple times in quick succession. - Defer the API call to an async worker so the synchronous handler can return 2xx fast and stay under any latency budget.
- Skip the hydration entirely for events where the
typeis enough to act on (e.g.session.status_terminatedtriggering a notification).
How Hookdeck Can Help: Event Gateway queues every event durably and lets your handler process them at whatever rate your downstream services tolerate. The 5-minute freshness window stops being a problem because Hookdeck verifies and queues events at the edge — your worker can fetch the full resource at its own pace.
At-least-once delivery means duplicates
The Problem: Claude retries failed deliveries with the same event.id. Your handler will receive duplicate events, and acting on them more than once can mean duplicate notifications, duplicate database writes, or duplicate downstream API calls.
Why It Happens: Anthropic prioritises eventual delivery over exactly-once semantics. There's no idempotency-key header — you're expected to dedupe on the top-level event.id.
Workarounds:
- Track processed
event.ids in a fast key-value store with a TTL longer than the retry window. - Make every side effect idempotent at the destination — use unique constraints, upserts, or natural keys instead of inserts.
- Don't rely on processing order to detect duplicates; events can arrive out of sequence.
How Hookdeck Can Help: Event Gateway dedupes events at the edge using configurable rules and only forwards unique events downstream. For a deeper background, see our guide to webhook idempotency.
No ordering guarantees
The Problem: Claude makes no guarantees about the order in which events arrive. A session.status_idled event may land before the session.outcome_evaluation_ended event that produced it. Naive state machines that assume sequential delivery will get into impossible states.
Why It Happens: Anthropic's delivery infrastructure is parallelised; ordering would require serialising deliveries per session, which would limit throughput.
Workarounds:
- Use the
created_attimestamp on the event envelope to sort. Don't rely on receipt order. - Treat each event as a transition trigger that re-fetches the current state from the API rather than as the authoritative current state itself.
- Build state machines that tolerate out-of-order transitions — for example, treat
terminatedas terminal regardless of which other events have arrived.
How Hookdeck Can Help: Event Gateway's queueing and replay let you re-process events in any order you choose during recovery. For deeper context on why ordering is hard and how to handle it, see our guide to webhook ordering.
Five-minute freshness window
The Problem: Webhook payloads older than five minutes are rejected by the SDK helper as a replay-protection measure. If your handler is slow, queued behind a backlog, or briefly down, deliveries can age out before you process them.
Why It Happens: The freshness window is what makes signature verification resistant to replay attacks — without it, a captured signed payload could be re-delivered indefinitely.
Workarounds:
- Acknowledge fast (return 2xx within milliseconds) and process asynchronously off a queue.
- Monitor end-to-end latency from
created_atto processing and alert on regressions. - Avoid processing-time work in the synchronous webhook handler; defer everything except verification and enqueue.
How Hookdeck Can Help: Event Gateway verifies the signature once at the edge and acknowledges Claude immediately. Your handler then consumes from Hookdeck's queue at whatever rate suits — the freshness window doesn't apply to Event Gateway's downstream forwarding because the verification has already happened.
3xx redirects are treated as failures
The Problem: Claude doesn't follow redirects. If your endpoint URL changes and the old hostname returns a 301 or 302, Claude marks the delivery as failed and counts it toward the auto-disable threshold.
Why It Happens: Following redirects to arbitrary URLs is a security risk and adds delivery latency.
Workarounds:
- Update the endpoint URL in the Console whenever your hostname changes. Don't rely on redirects.
- Use a stable, dedicated domain for webhook ingress so renames and migrations don't cascade into endpoint configuration.
How Hookdeck Can Help: Event Gateway gives you a stable HTTPS URL that doesn't change when your application's hostname does. Internal routing is a Hookdeck-side configuration, not a Claude-side endpoint update.
Auto-disable after ~20 consecutive failures
The Problem: A flaky downstream service can trip the auto-disable threshold and silently blackhole all your agent webhooks. Re-enabling requires manual action in the Console.
Why It Happens: Anthropic doesn't want to keep retrying against an endpoint that's clearly broken.
Workarounds:
- Add monitoring on your endpoint's health and alert before the threshold is reached.
- Build runbooks for re-enabling endpoints after recovery.
- Make sure failures are truly failures — don't return 4xx or 5xx for application errors that should be acknowledged and retried internally.
How Hookdeck Can Help: Event Gateway absorbs the volatility of a flaky downstream consumer. Claude only sees Event Gateway's edge response, which stays healthy as long as Event Gateway does. Your downstream can fail for hours and Claude's auto-disable counter never increments.
Inbound trigger has no idempotency key
The Problem: The routines.fire endpoint that lets external systems trigger Claude Code routines has no idempotency key. Every retried call creates a new Claude session — and burns quota.
Why It Happens: Each call is intentionally a session-creation operation, not a "ensure session exists" operation.
Workarounds:
- Dedupe on the upstream provider's event ID before calling
routines.fire. - Track in-flight session creations in a fast key-value store and short-circuit duplicates.
How Hookdeck Can Help: Event Gateway dedupes on the upstream event ID at the edge before any call to routines.fire. A single GitHub event spawns one Claude session, not three. See our walk-through of GitHub, Trigger.dev, and Claude for the full pattern.
Local development friction
The Problem: Webhook development needs a public HTTPS endpoint, signed payloads, and a way to replay events while you iterate. Standard tunneling tools don't preserve event history or let you re-deliver events on demand.
Why It Happens: Webhooks were designed for server-to-server delivery, not for local development workflows.
Workarounds:
- Use a tunneling tool to expose
localhostover HTTPS. - Trigger live events to test changes (slow and costly).
- Save raw payloads to disk and re-POST them manually with curl.
How Hookdeck Can Help: The Hookdeck CLI gives you a stable public HTTPS URL, full request inspection, and one-click replay. CLI users activate at materially higher rates than non-CLI users — the development feedback loop is genuinely the difference between an integration that ships and one that stalls. See our guide to testing and replaying Claude webhooks locally.
Best practices
Always verify webhook signatures
Use the Anthropic SDK helper to verify both the HMAC and the five-minute freshness window. Never trust an unsigned payload, never skip the freshness check, and never use a non-constant-time string comparison if you're verifying manually.
Return 2xx status codes quickly
Acknowledge within milliseconds and defer work to an async queue. The five-minute freshness window plus the auto-disable threshold mean slow handlers fail twice — once because deliveries age out, and once because retries pile up against a struggling endpoint.
Subscribe only to required events
Each endpoint subscribes per-event-type. Don't blanket-subscribe to every event family — it inflates your processing volume and forces your handler to filter events it never needed in the first place. Add subscriptions when you need them.
Dedupe on event.id
Track processed event.ids with a TTL longer than Anthropic's retry window. The top-level event.id is unique per event, not per delivery — retries reuse it, which is exactly what makes it usable as an idempotency key.
Sort by created_at when ordering matters
Don't rely on receipt order. If your handler needs to apply transitions in sequence, sort by the created_at field on the event envelope. Better still, treat each event as a "go check the current state" trigger rather than a sequential message.
Process events asynchronously
Verify, enqueue, and acknowledge synchronously; do everything else off a queue. This keeps your handler under the freshness window, makes retries safe, and lets you scale workers independently of webhook ingress. For background, see our guide on why to implement asynchronous processing for webhooks.
Implement reconciliation jobs
Webhooks are a notification mechanism, not a system of record. Periodically reconcile against the Anthropic API to catch any events that were dropped, auto-disabled, or processed incorrectly. The thin-payload pattern actually helps here — your reconciliation job is already shaped like the rest of your handler.
Validate payloads
Even after signature verification, validate the parsed event against an expected schema. Anthropic adds new event types over time, and a handler that assumes every payload has fields it expects will break the first time a new event family ships.
Conclusion
Claude Managed Agent webhooks are how agent activity becomes addressable from the rest of your stack. Anthropic ships them with sensible defaults — HMAC signing, replay protection, at-least-once delivery, thin payloads, SDK helpers in seven languages. However, the operational responsibilities of running them in production are non-trivial: deduplication, ordering tolerance, freshness budgets, auto-disable, secret rotation, and the local-development feedback loop are all problems your team will own unless you offload them.
For teams that want to focus on what their agents do rather than on the infrastructure that delivers their events, Hookdeck handles verification, deduplication, queueing, retries, replay, and observability at the edge — so your application only ever sees verified, deduplicated, durably-queued events.
Get started with Hookdeck Event Gateway for free and start handling Claude webhooks reliably in minutes.