Add WhatsApp to your app with Claude Code: connect it, then review four things
Connect Claude Code to SendFromChat with one command, ask it to add WhatsApp order notifications, then check what it wrote: where the key lives, retries, STOP replies and templates.
October 24, 2026 · 7 min read
Claude Code can write the WhatsApp part of your app for you: a send function, a webhook route, the template names. It can do that well only if it can see your real, approved templates and your account. The part that stays with you is the review, because a WhatsApp integration fails in quiet ways: a retry sends twice, a customer who replied STOP gets another message, a webhook accepts anything.
This guide shows how to connect Claude Code, what to ask it, and the four things to check before you merge what it wrote.
SendFromChat is an independent product and is not affiliated with Meta or WhatsApp. It runs on Meta's official WhatsApp Cloud API, and you keep your own WhatsApp Business account.
Not run against a live API key. The code below comes from our documentation and the existing order notifications guide. We have not run it against a live key for this guide, and it is not output we captured from Claude Code. What Claude Code writes for you will differ, which is the reason for the review section. Treat every sample as a shape to compare against, and test with a test key first.
What you need
- A SendFromChat workspace (30-day free trial, no card) with a WhatsApp number connected.
- Claude Code installed, and a project to change (the examples use Node).
- An approved template for the message you want to send, for example an order update. Messages you start outside a 24-hour conversation window must use one (the 24-hour window, template rules). You can ask Claude to draft and submit it through the same connection.
- Customers who agreed to WhatsApp updates (how to collect opt-in).
1. Connect Claude Code
One command:
claude mcp add --transport http sendfromchat https://sendfromchat.com/api/mcp
Then run /mcp inside Claude Code, choose sendfromchat → Authenticate, sign in in the browser and click Allow. There is nothing secret to copy or paste. You can see or disconnect the connection under Settings → Connected apps in the dashboard, and disconnecting stops it immediately. More clients are covered in connect your assistant.
2. Ask for the integration
Be specific about the event, the language and the template. Three prompts that work as a starting point:
- "Add WhatsApp order notifications to this app. Send my approved
order_shippedtemplate when an order's status changes to shipped. Use the SendFromChat REST API." - "Add a sign-in code over WhatsApp using an Authentication template, and tell me which template you used." (See OTP login codes over WhatsApp.)
- "Add a webhook route that receives replies and delivery status, and verify the signature." (See receive replies with webhooks.)
According to the REST API docs, Claude Code fetches your approved templates and writes the integration for your language. Read the diff before you accept it.
3. Where the key goes
The REST API needs an API key. Per the docs, you do not copy one: ask Claude Code to set up SendFromChat in your project, and it creates the key and saves it straight into .env.local, or into your host's secrets (Vercel, Netlify, Railway, Fly.io, Heroku, Cloudflare Workers or GitHub Actions) through that platform's CLI. The docs say the key never appears in the conversation; Claude only handles a one-time code that expires in ten minutes, and live keys are created only after you confirm (REST API: authentication).
Start with a test key (sfc_test_…). It runs the same checks as a real send, for plan, opt-outs, template and the 24-hour window, then stops. Nothing is sent. Swap in a live key when the output looks right.
4. Review these four things
1. The key stays on your server
Open the diff and search it for the key. It should be read from an environment variable, in server code only (an API route, a server action, a worker). It should not be in client or mobile code, in a committed file, or in a log line. Check that .env.local is in .gitignore. Our docs put it plainly: keep keys on your server, never in browser or mobile-app code.
2. One idempotency key per event
A retry after a timeout must not send a second message. The send call should carry an Idempotency-Key header built from the event (for example order-4821-shipped), not from the attempt (a timestamp or a random ID would defeat it). With the same key and body, a retry returns the first response with Idempotent-Replayed: true instead of sending again.
A send function you can compare against:
// lib/whatsapp.js — runs on your server only
export async function notifyShipped(order) {
const res = await fetch("https://sendfromchat.com/api/v1/messages", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SENDFROMCHAT_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": `order-${order.id}-shipped`,
},
body: JSON.stringify({
to: order.customerPhone, // international format, e.g. +491512345678
template: {
name: "order_shipped",
language: "en_US",
variables: [order.customerName, `#${order.number}`, order.trackingUrl],
},
}),
});
const body = await res.json();
if (!res.ok) throw new Error(`${body.error?.type}: ${body.error?.message}`);
return body; // { id: "wamid…", status: "accepted", … }
}
Also check how errors are handled. opted_out, window_closed and template_not_approved are WhatsApp's rules, not bugs: log them and move on instead of retrying forever. warmup_limit means a new number reached today's cap, so retry later. The full list is in the error table.
3. STOP is respected
When a customer replies STOP, they are opted out and the API refuses template sends to them with opted_out. Your app should not fight that. Two things to look for in what Claude wrote:
- It does not catch
opted_outand resend or switch to another channel without the person's consent. - If you keep your own customer table, it listens for the
contact.opted_outwebhook event (andcontact.opted_in) and updates its own record, so your other systems stop messaging that number too.
4. The template is approved, and the window is respected
Outside a 24-hour conversation window, only an approved template can start a message. Check that the template name and language in the code match one that is approved in your workspace, and that the number and order of variables match the template's {{1}}, {{2}}… placeholders. Then check what happens when the API refuses: template_not_approved and window_closed (both HTTP 422) should be logged and handled, not retried in a loop. Free-form text only delivers within 24 hours of the person's last message to you (the window explained).
Also check: webhooks are verified
If Claude Code added a webhook route for replies and delivery status, check the signature handling too. Each request carries a SendFromChat-Signature header: an HMAC-SHA256 of the timestamp and the raw request body, keyed with your endpoint's secret. A route that parses the JSON first and then verifies will fail, or worse, skip the check. Look for these in the diff:
- The check runs on the raw body, before parsing.
- The comparison is constant-time.
- Timestamps older than five minutes are rejected.
- Repeated event
idvalues are skipped, because deliveries can repeat. - The route answers with a
2xxwithin five seconds and does the slow work afterwards.
The REST API reference has a verification function you can hand to Claude Code as the reference to match.
Checklist before you go live
- The key is read from the environment, server-side only, and not committed.
- The
Idempotency-Keycomes from the event. opted_outis respected, andwindow_closed,template_not_approvedandwarmup_limitare handled, not retried blindly.- The template is approved and matches the variables the code sends.
- If there is a webhook, it verifies the signature on the raw body and rejects old timestamps.
- You tested with a test key, then sent one real message to your own number with a live key.
What we did not test
- We did not run these samples, or the prompts above, against a live API key for this guide.
- We have not captured what Claude Code produces for them. Its output varies with your project and the model, so review it every time.
- Meta decides template approval and delivery. SendFromChat cannot guarantee either.
Try it
Start the 30-day free trial (no card), connect your number and run the command in step 1. If you want the full walkthrough of one message type first, read WhatsApp order notifications from your app. If you build with an assistant but not in code, see send WhatsApp from Claude and ChatGPT.
Sources
Checked in the SendFromChat documentation on 24 Oct 2026: connect your assistant, REST API, and the order notifications guide.
Do this from your AI assistant
SendFromChat connects Claude, ChatGPT, Cursor or any MCP client to the WhatsApp Business API — templates, bulk sends, inbox, chatbots and sequences, just by asking. 30 days free, no card.
Start freeMore guides
WhatsApp API phone number format for European numbers (E.164, the leading 0 and the missing +)
How to write a customer's phone number for the WhatsApp Business API: the + and country code, which European countries drop the leading 0, why Italy keeps it, and how to normalise numbers in code.
Questions to ask a WhatsApp provider before you sign: who owns your account, number and templates
Three questions to put to any WhatsApp Business API provider (BSP or Tech Provider) before you sign — who owns the WhatsApp Business Account, whether you can leave with your number and templates, and who bills Meta's charges.
Build a WhatsApp reply assistant with Claude (with a human hand-off)
Answer customer WhatsApp messages about your own business with the Claude API. A webhook, a restricted system prompt, a hand-off to a person, STOP respected and replies sent inside the 24-hour window.
WhatsApp appointment reminders from your app: a working example (with clock-change-safe timing)
Send WhatsApp appointment reminders from your own code with a REST API. A scheduled job, an approved template, time zones that survive the October clock change, safe retries and CONFIRM / CANCEL / STOP replies.