← Blog

Claude API WhatsApp Business API Integration Guide

2026-11-07 · 5 min read · SubToAPI Team

What this integration actually involves

Connecting Claude to WhatsApp Business API means building a bridge between two independent systems: Meta's WhatsApp Business Platform (which handles phone numbers, message delivery, and templates) and Claude's API (which generates the actual replies). There's no native "Claude for WhatsApp" product — you write a small webhook service that receives incoming WhatsApp messages, forwards the text to Claude, and sends Claude's response back through the WhatsApp API.

This is a common pattern for customer support bots, order status assistants, and internal tools where WhatsApp is the primary channel. The architecture is straightforward once you understand the three moving parts: WhatsApp webhooks, your backend, and the Claude API call itself.

The three components you need

1. A WhatsApp Business API account. You get this through Meta directly or through a Business Solution Provider (BSP) like Twilio, 360dialog, or Infobip. Meta's own Cloud API is free to set up and is the easiest path for developers testing an integration.

2. A webhook endpoint. WhatsApp sends incoming messages to a URL you control via HTTPS POST. This endpoint needs to verify a challenge token on setup and then parse incoming message payloads in real time.

3. Access to Claude. This is where you call the model with the user's message and send the reply back. You can go directly against Anthropic's API, or use a provider like SubToAPI that wraps your existing Claude access in a standard HTTPS API with application keys — useful if you want usage tracking and team-level API keys without managing Anthropic billing credentials directly in your webhook service.

Setting up the webhook

A minimal Node.js/Express webhook looks like this:

import express from "express";
const app = express();
app.use(express.json());

// Verification step required by Meta
app.get("/webhook", (req, res) => {
  const { "hub.mode": mode, "hub.verify_token": token, "hub.challenge": challenge } = req.query;
  if (mode === "subscribe" && token === process.env.VERIFY_TOKEN) {
    return res.status(200).send(challenge);
  }
  res.sendStatus(403);
});

// Incoming message handler
app.post("/webhook", async (req, res) => {
  res.sendStatus(200); // ack immediately, WhatsApp expects fast response
  const entry = req.body.entry?.[0]?.changes?.[0]?.value;
  const message = entry?.messages?.[0];
  if (!message || message.type !== "text") return;

  const from = message.from;
  const text = message.text.body;

  const reply = await getClaudeReply(text);
  await sendWhatsAppMessage(from, reply);
});

app.listen(3000);

Acknowledge the webhook immediately with a 200. WhatsApp retries if your server is slow or times out, which can cause duplicate messages if your Claude call takes a while.

Calling Claude

Here's the part that generates the actual response. If you're using SubToAPI, the call is a plain HTTPS request with your sub_live_... key:

async function getClaudeReply(userText) {
  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: 400,
      system: "You are a WhatsApp support assistant. Keep replies under 300 characters.",
      messages: [{ role: "user", content: userText }],
    }),
  });

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

The system prompt matters more here than in most integrations. WhatsApp messages are read on a phone, usually glanced at rather than scrolled through, so instruct the model to keep replies short, avoid markdown formatting (WhatsApp doesn't render it the same way), and skip long preambles. See /docs/messages for the full request shape.

Sending the reply back

Sending a message through Meta's Cloud API:

async function sendWhatsAppMessage(to, text) {
  await fetch(
    `https://graph.facebook.com/v19.0/${process.env.PHONE_NUMBER_ID}/messages`,
    {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${process.env.WHATSAPP_TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        messaging_product: "whatsapp",
        to,
        text: { body: text },
      }),
    }
  );
}

Handling conversation state

WhatsApp doesn't give you conversation history — each webhook event is a single message. If you want multi-turn context, you need to store the conversation yourself, keyed by the sender's phone number, and pass prior turns into the messages array on each Claude call. A simple approach: keep the last 6–10 messages per phone number in Redis or a database table with a TTL, and prepend them to each new request.

Streaming isn't practical here

WhatsApp doesn't support partial message updates the way a chat UI does — each message you send is final and immutable. So even though SubToAPI supports streaming responses (see /docs/streaming), there's no benefit to streaming in a WhatsApp bot. Wait for the full Claude response, then send one WhatsApp message. If you need to show "typing" status, WhatsApp's Cloud API has a separate typing indicator endpoint you can call while Claude generates.

Tool use for real actions

If your bot needs to do more than chat — check an order status, look up inventory, schedule something — Claude's tool use lets the model call functions you define and incorporate the result into its reply before you send anything to the user. This keeps the WhatsApp side simple: your webhook just waits for the final text output after the tool loop completes. Details on defining tools are in /docs/tools.

Rate limits and reliability

Production WhatsApp bots need to handle bursts (a marketing campaign driving replies) without dropping messages. Queue incoming webhook events and process them asynchronously rather than calling Claude synchronously inside the webhook handler, especially if you expect spikes. On the Claude side, using an API layer with built-in usage metadata — like SubToAPI's dashboard — makes it easier to see which phone numbers or message volumes are driving cost, since WhatsApp traffic patterns are bursty and hard to predict from app-level logs alone. Check /pricing if you're estimating cost per seat for a support team using this setup, or start with a free trial at /signup to test the full flow end to end.

questions

Do I need Anthropic's API directly, or can I use a wrapper service? Either works. Calling Anthropic directly gives you the lowest-level control; a wrapper like SubToAPI (/docs/quickstart) gives you a standard HTTPS API, application-level keys, and usage tracking without managing raw Anthropic credentials in your webhook service.

Can Claude handle WhatsApp media messages like images or voice notes? Yes, if your webhook extracts the media and sends it appropriately — images can go through Claude's vision input, voice notes typically need transcription first since Claude doesn't accept raw audio.

How do I keep Claude's replies within WhatsApp's character and formatting limits? Set a strict system prompt instructing short, plain-text replies, and enforce a max_tokens cap in your request to avoid overly long generations that get truncated oddly on the WhatsApp side.

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 →