Claude API Webhooks — Async Result Delivery

How to use webhooks with the Anthropic API in 2026. Cover the Batch API callback flow, verifying webhook signatures, and a complete Express/Flask receiver pattern.

🔥 Launch tonight — Power Prompts PDF 50p (just 50p tonight)30 battle-tested Claude Code prompts · 8 pages · paste into CLAUDE.md · price reverts to £5

Anthropic's real-time messages.create endpoint is request/response — there is no webhook on individual completions. Webhooks come into play for async workloads: the Batch API, long-running jobs, and any pipeline where you want a push notification when work finishes rather than polling.

Where webhooks fit in the Claude API surface

SurfacePatternWebhook?
messages.create (real-time)HTTP request/response, streaming or fullNo — synchronous
Batch APISubmit batch, poll status OR receive webhook on completionYes
Files API uploadsUpload, then reference by file_idNo
Claude.ai / Console eventsOrg/billing eventsNo public webhook product in 2026

Batch API webhook flow

  1. POST to /v1/messages/batches with a webhook_url field on the request.
  2. Anthropic accepts the batch and returns a batch_id.
  3. When the batch completes (typically minutes to 24h), Anthropic POSTs to your webhook_url with the batch metadata.
  4. Your handler verifies the signature, then GETs /v1/messages/batches/<id>/results to download the per-message outputs.

The webhook does not contain the full results — it is a completion notification. You still pull the output file via the API. This is identical to the OpenAI Batch + S3 pattern.

Verifying a Claude webhook (Node/Express)

import express from "express";
import crypto from "crypto";

const app = express();
const SECRET = process.env.ANTHROPIC_WEBHOOK_SECRET;

app.post("/anthropic-webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const sig = req.header("anthropic-signature");
    const ts  = req.header("anthropic-timestamp");
    const payload = ts + "." + req.body.toString("utf8");
    const expected = crypto
      .createHmac("sha256", SECRET)
      .update(payload)
      .digest("hex");

    if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
      return res.status(401).end();
    }
    // 5-minute replay window
    if (Math.abs(Date.now()/1000 - Number(ts)) > 300) {
      return res.status(401).end();
    }

    const event = JSON.parse(req.body.toString("utf8"));
    if (event.type === "batch.completed") {
      enqueueBatchResultDownload(event.data.batch_id);
    }
    res.status(204).end();
  }
);

Idempotency and retries

Anthropic retries webhook delivery on non-2xx responses for up to 24 hours with exponential backoff. Your handler must be idempotent: dedupe by event.id in a small store (Redis SET with 7-day TTL is sufficient). If you 2xx but then crash before processing, you will not get a re-delivery — process inside the same transaction as the 2xx, or queue first and ack later.

Local development — webhook tunnels

Use ngrok or cloudflared tunnel to expose your local handler to Anthropic's outbound webhook traffic during development. Set webhook_url on the batch request to the public tunnel URL. Production should always use a stable public endpoint (DNS, not an ngrok URL).

If you don't want webhooks: polling

The Batch API also supports polling. GET /v1/messages/batches/<id> returns processing_status (in_progress / completed / canceled / expired / failed). Poll every 30–60 seconds. Webhooks are strictly better for latency and infra cost; polling is fine for low-volume or batch-completion-doesn't-matter workloads.

What about real-time webhooks?

For real-time completions, the stream=true response is the Anthropic equivalent of a webhook: each SSE event is a partial completion. For an architecture where Claude finishes asynchronously and pushes the final answer, the canonical pattern is: streaming response → your server collects tokens → your server fires its own webhook to a downstream consumer.

For background on cost differences between sync, batch, and streaming, see Batch vs Streaming: Cost.

Frequently asked questions

Does the Anthropic Claude API support webhooks?
Yes, for the Batch API. You set webhook_url on the batch submission and Anthropic POSTs a completion notification when the batch finishes. There is no webhook for individual messages.create calls — those are synchronous request/response.
How do I verify a Claude webhook signature?
Anthropic signs webhooks with HMAC-SHA256 over the timestamp + raw body, using a shared secret. Compute the same HMAC with your stored secret and compare in constant time. Also reject events whose timestamp is more than 5 minutes from now to prevent replay.
What happens if my webhook endpoint is down when Anthropic tries to deliver?
Anthropic retries with exponential backoff for up to 24 hours. After the retry window, the event is dropped. You can still recover by polling the batch status endpoint manually.
Is there a Stripe-style /v1/webhook_endpoints management API for Claude?
No central webhook-endpoint registry in 2026 — the webhook_url is set per-batch on submission. This is simpler but means rotation requires updating your submission code, not a control-plane API call.
Can I use webhooks for streaming completions?
No. For real-time streaming, use stream=true on messages.create and consume server-sent events directly. The webhook surface is async-only.

Free tools

Cost Calculator → Prompt-Pricing Recommender → Diff Summarizer → Skills Browser →

Related

Claude Opus 4.7 vs Sonnet 4.6 Pricing (2026 Comparison)How Much Does Claude Cost? (2026 API Pricing Guide)Claude Prompt Caching: 90% Cost Savings Explained (2026)Claude API Cost Calculator: Estimate Your Anthropic BillClaude vs GPT-4 Pricing: 2026 API Cost Comparison