← Blog

Claude API Webhook Integration Setup Guide

2026-09-30 · 5 min read · SubToAPI Team

Does the Claude API have webhooks?

No — the Claude API is a synchronous request/response API. You send a prompt, you get a completion (or a stream of tokens) back on the same connection. There's no built-in event system that pushes notifications to a URL when something happens on Anthropic's side.

So when developers search for "Claude API webhook integration," they're almost always trying to solve one of two real problems: triggering a Claude call when an external event happens (a new Stripe payment, a GitHub PR, a Slack message), or notifying another system when a Claude response is ready, especially for long-running or background jobs where you don't want to hold a connection open. This guide covers both patterns with working code.

Pattern 1: Inbound webhooks that trigger Claude

This is the most common setup. Something happens in a third-party system, it POSTs to your server, and your server calls Claude to process it.

Typical flow:

  1. Receive and verify the webhook payload
  2. Extract the relevant data (an email body, a support ticket, a commit diff)
  3. Call the Claude API with that data as context
  4. Do something with the result — save it, reply, forward it

Here's a minimal Express endpoint that receives a webhook and calls Claude:

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

const app = express();
app.use(express.json({ verify: rawBodySaver }));

function rawBodySaver(req, res, buf) {
  req.rawBody = buf;
}

function verifySignature(req, secret) {
  const signature = req.headers["x-webhook-signature"];
  const expected = crypto
    .createHmac("sha256", secret)
    .update(req.rawBody)
    .digest("hex");
  return signature === expected;
}

app.post("/webhooks/incoming", async (req, res) => {
  if (!verifySignature(req, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send("invalid signature");
  }

  // Acknowledge immediately, process async
  res.status(202).send("accepted");

  const payload = req.body;
  const response = await fetch("https://api.subtoapi.app/v1/messages", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SUBTOAPI_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "claude-sonnet-4-5",
      max_tokens: 1024,
      messages: [
        { role: "user", content: `Summarize this support ticket:\n\n${payload.text}` },
      ],
    }),
  });

  const data = await response.json();
  await saveResult(payload.id, data.content[0].text);
});

app.listen(3000);

Two details matter here. First, verify the signature before trusting the payload — never call an LLM with unverified input from a public endpoint that also has your API key attached to it. Second, acknowledge the webhook immediately and process it asynchronously. Most webhook providers (Stripe, GitHub, Slack) expect a response within a few seconds and will retry if you don't reply, which can lead to duplicate Claude calls if your processing takes longer than that.

Pattern 2: Outbound "completion" webhooks

If you're running Claude calls in a background job — batch summarization, document processing, a queue worker — you often want to notify another service when the result is ready instead of polling. This isn't something Claude provides natively; you build it yourself around the API call.

async function processJobWithCallback(job) {
  const response = await fetch("https://api.subtoapi.app/v1/messages", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SUBTOAPI_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "claude-sonnet-4-5",
      max_tokens: 2048,
      messages: job.messages,
    }),
  });

  const result = await response.json();

  // Notify the requesting system that the job is done
  await fetch(job.callback_url, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      job_id: job.id,
      status: "completed",
      output: result.content[0].text,
      usage: result.usage,
    }),
  });
}

This pattern is useful for API products where a client submits a job, gets a job_id back immediately, and receives a webhook when it's done rather than holding a request open for 30+ seconds. Include the job_id, a status field, and usage metadata in the callback payload so downstream systems can reconcile results and track cost per job.

Handling streaming inside a webhook flow

If your webhook consumer needs partial output rather than waiting for the full response, don't try to forward a raw stream through a webhook call — webhooks are single request/response, not persistent connections. Instead, buffer the stream server-side and either send incremental webhook pings at intervals or push chunks over a WebSocket/SSE connection to the actual client. The full mechanics of consuming streamed responses are covered in /docs/streaming.

Retries, idempotency, and failure handling

A production webhook integration needs to handle three failure modes:

Where SubToAPI fits

SubToAPI doesn't add webhook events to the Claude API itself, but it gives you a stable, authenticated HTTPS endpoint (sub_live_... keys) to call from your webhook handlers, along with per-request usage metadata you can attach to your own callback payloads — useful for billing jobs back to customers or tracking cost per webhook-triggered call. Streaming and tool use both work the same way as direct API access; see /docs/messages and /docs/tools for request formats, or /docs/quickstart to get a key running in a few minutes. Plans start at €9/month on the Solo tier, with team seats on the Team and Scale plans — details at /pricing.

questions

Does Anthropic's Claude API support native webhooks? No. It's a request/response API with no built-in event push system. Webhook behavior has to be built on your side, either by receiving inbound webhooks that trigger a Claude call or by sending outbound callbacks when your own background job finishes.

How do I avoid duplicate Claude API calls from webhook retries? Store the event ID from each incoming webhook and check it before processing. Return a 2xx response as fast as possible and do the actual Claude call asynchronously so the sender doesn't retry due to a timeout.

Can I stream Claude's response directly through a webhook? Not directly — webhooks are single-shot HTTP calls. Buffer the streamed response server-side, then either send periodic webhook updates or relay chunks to the client over SSE or WebSockets. See /docs/streaming for handling the stream itself.

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 →