← Blog

Claude API Webhook Integration Tutorial (Step-by-Step)

2026-10-03 · 5 min read · SubToAPI Team

Does the Claude API have webhooks?

No — the Claude API is a request/response HTTP API. You send a message, you get a completion back (or a stream of tokens). There's no built-in mechanism where Anthropic pushes events to a URL you register, unlike payment processors or GitHub.

What developers usually mean by "Claude API webhook integration" is one of two things: triggering Claude from an incoming webhook (a GitHub push, a Stripe event, a form submission) or notifying another system when Claude finishes generating a response, especially for long-running jobs. Both are easy to build yourself with a thin integration layer. This tutorial walks through that pattern end to end.

The architecture

A typical Claude webhook integration has three parts:

  1. Inbound webhook receiver — an HTTP endpoint that accepts events from an external system (Slack, GitHub, Zapier, your own app).
  2. Claude call — the receiver extracts relevant data from the event, builds a prompt, and calls the Messages API.
  3. Outbound webhook (callback) — once Claude responds, you POST the result to another URL, which could be the same system or a different one (e.g., post a Slack message, update a ticket, write to a database and notify a dashboard).

For short requests this can all happen inside one HTTP handler. For anything that takes more than a few seconds, you should acknowledge the inbound webhook immediately and process the Claude call asynchronously, then fire the outbound webhook when it's done — exactly like how Stripe or GitHub expect you to respond fast and do work later.

Step 1: Build the inbound receiver

Here's a minimal Express endpoint that receives a webhook, verifies its signature, and queues work:

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

const app = express();
app.use(express.json());

app.post("/webhooks/inbound", (req, res) => {
  const signature = req.headers["x-webhook-signature"];
  const expected = crypto
    .createHmac("sha256", process.env.WEBHOOK_SECRET)
    .update(JSON.stringify(req.body))
    .digest("hex");

  if (signature !== expected) {
    return res.status(401).send("invalid signature");
  }

  // Acknowledge immediately, process async
  res.status(202).send("accepted");
  processWithClaude(req.body).catch(console.error);
});

app.listen(3000);

Always verify signatures on inbound webhooks. Most providers (GitHub, Stripe, Slack) sign their payloads with HMAC — don't skip this check just because it's "internal."

Step 2: Call Claude and extract a response

Here's the processing function using the Messages API directly:

async function processWithClaude(event) {
  const response = await fetch("https://api.anthropic.com/v1/messages", {
    method: "POST",
    headers: {
      "x-api-key": process.env.ANTHROPIC_API_KEY,
      "anthropic-version": "2023-06-01",
      "content-type": "application/json",
    },
    body: JSON.stringify({
      model: "claude-sonnet-4-5",
      max_tokens: 500,
      messages: [
        {
          role: "user",
          content: `Summarize this event and suggest a next action:\n${JSON.stringify(event)}`,
        },
      ],
    }),
  });

  const data = await response.json();
  const result = data.content[0].text;

  await sendOutboundWebhook(result, event);
}

If you're running this behind SubToAPI instead of calling Anthropic directly, the call looks almost identical — you swap the endpoint and auth header, and you also get usage metadata (tokens, cost) back on every response, which is useful for logging alongside each webhook event:

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5",
    "max_tokens": 500,
    "messages": [{"role": "user", "content": "Summarize this event..."}]
  }'

See /docs/messages for the full request/response shape.

Step 3: Fire the outbound webhook

Once you have Claude's output, POST it wherever it needs to go:

async function sendOutboundWebhook(result, originalEvent) {
  await fetch(process.env.OUTBOUND_WEBHOOK_URL, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({
      source_event_id: originalEvent.id,
      summary: result,
      timestamp: new Date().toISOString(),
    }),
  });
}

Include the original event ID so the receiving system can correlate the callback with the request that triggered it — this matters a lot once you have retries in play.

Handling retries and idempotency

Two failure modes to design for:

A simple way to make this safe: persist {event_id, status, result} in a database row as soon as Claude responds, then have a separate small worker that retries undelivered webhooks on a schedule. This decouples "did Claude finish" from "did the callback get delivered."

Streaming inside a webhook flow

If you want token-by-token updates (e.g., streaming into a chat UI that's itself driven by a webhook trigger), you can't stream over a single outbound webhook POST — webhooks are one-shot. Instead, stream the Claude response server-side and push partial updates over a WebSocket or Server-Sent Events connection to the client, while using the webhook only as the trigger. SubToAPI's streaming endpoint works the same way Anthropic's does under the hood — see /docs/streaming if you're building this.

Where SubToAPI fits

If you're integrating Claude into several webhook-driven workflows (CI pipelines, support ticket triage, form processing), managing raw Anthropic keys across each service gets messy fast — no per-service usage visibility, no easy key rotation, no team access control. SubToAPI wraps the same Claude models behind sub_live_... API keys with per-key usage metadata and team seats, so each webhook integration can have its own scoped key and you can see exactly which one is driving cost. Start with a free trial at /signup, or check /pricing and /docs/quickstart to see how the setup compares to calling Anthropic directly. Tool use (function calling) works the same way too — see /docs/tools if your webhook handler needs Claude to call external functions as part of the flow.

Questions

Does Anthropic offer native webhook events for the Claude API? No. The API is purely request/response (or streaming within a single connection). Any "webhook" behavior — triggering on an external event or notifying another system when a response is ready — has to be built in your own integration layer.

How do I handle Claude calls that take longer than my webhook timeout? Acknowledge the inbound webhook immediately with a 2xx response, then process the Claude call asynchronously in a background job or queue, and deliver the result via a separate outbound webhook when it's ready.

Can I verify that an outbound webhook I send was actually delivered? Only if the receiving endpoint responds with a success status you check. Treat non-2xx responses as failures, log them with the original event ID, and retry on a schedule rather than blocking the Claude call on delivery success.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →