Use the WhatsApp Cloud API for MainSDK notifications and WhatsApp sign-in - Meta business setup, templates, tokens, webhooks, main.yaml and swapping.
The WhatsApp Cloud API is Meta's hosted API for WhatsApp Business. In a MainSDK app it is the default WhatsApp channel driver for notify, so an order update or a payment reminder can reach a person on WhatsApp, and it is the default sender for WhatsApp one-time codes in auth. Pick it when your users live on WhatsApp, as many do in India, and you want to connect to Meta directly rather than through a messaging provider such as Bird or MSG91.
Interfaces it serves
Pick WhatsApp in main.yaml for any of these. Your calls stay the same, and you can swap later.
notifyBuilding for 0.3Driver whatsapp-cloud WhatsApp Cloud API
notify:
driver: whatsapp-cloudBefore you start
You need a Meta business account and a phone number that is not already registered on another WhatsApp account.
- Set up WhatsApp Business. In Meta's developer tools, create an app with the WhatsApp product. It links to a WhatsApp Business Account owned by your business. Meta may ask you to verify the business before you can send at volume or display your business name.
- Add a phone number. Register the number your app sends from in the WhatsApp Business Account and note its phone number ID. Meta also gives you a test number for development, which can send to a few recipients you register.
- Create message templates. Business-initiated messages, sent outside the window that opens when a person messages you, must use a template Meta has approved. Create one template per notification (for example
order_shipped), in each language you send, and wait for approval. Sign-in codes use a template in Meta's authentication category. - Create an access token. The temporary token Meta shows during setup expires quickly. For stage and production, create a long-lived token for a system user with access to the WhatsApp Business Account.
- Webhook (optional). Meta sends delivery status and incoming messages to a webhook URL. The
notifyWhatsApp driver does not read them: it records the provider's message id when Meta accepts the message, and nothing after.
Store the token in your secrets manager, never in code or a committed env file.
Configure
For notifications, name the driver on the whatsapp channel:
notify:
channels: [whatsapp, sms, email]
whatsapp: whatsapp-cloud
whatsapp-cloud:
phone-number-id: "1234567890" # the number your app sends from
token: secret:WHATSAPP_TOKEN # a system user's access token; this is the default name
version: v21.0 # the Graph API version; this is the default
templates:
order.shipped:
whatsapp: { name: order_shipped, language: en, params: [orderId] }nameis the template name Meta approved.languageis its language code (defaulten; alocaleon the call or the person overrides it).paramslists thedatakeys, in order, that fill the template's body variables.- A notification with no
whatsapptemplate is skipped on WhatsApp with the reasonno-template. - A literal token fails validation with
MAIN-CONFIG-0004.
What the driver sends
Each message is one POST https://graph.facebook.com/<version>/<phone-number-id>/messages with Authorization: Bearer <token>. The JSON body holds messaging_product: "whatsapp", to (digits, no plus) and type: "template" with the template name, the language code and the body parameters in order. The id Meta returns becomes the delivery's providerId. The call goes through MainSDK's network layer, is never retried by it, does not follow redirects, and reads at most 64 KiB of the answer. A 429, a 5xx or no answer is a retryable MAIN-NOTIFY-0007 and notify tries the next channel.
For sign-in, add whatsapp to the methods and pick the sender:
auth:
driver: github
methods: [passkeys, whatsapp]
whatsapp: { driver: whatsapp-cloud }India users usually expect WhatsApp first. Put it first in the market overlay:
notify:
channels: [whatsapp, sms, push, email]
auth:
methods: [whatsapp, passkeys]Use it
Notify a person. WhatsApp is used when it is the first channel they allow and they have recorded consent; the template name maps to your approved template.
import { notify } from "mainsdk";
export async function orderShipped(user: { id: string }, orderId: string) {
return notify(user, {
template: "order.shipped",
data: { orderId },
category: "orders",
idempotencyKey: `order.shipped:${orderId}`,
});
}Sign in with a WhatsApp one-time code:
"use server";
import { auth } from "mainsdk";
export async function sendCode(phone: string) {
const { challengeId } = await auth.whatsapp.send(phone);
return challengeId;
}
export async function confirmCode(challengeId: string, code: string) {
return auth.whatsapp.verify(challengeId, code); // MAIN-AUTH-0006 on reuse, expiry or too many tries
}The MainSDK template name (order.shipped) maps to the Meta template name and language through notify.templates.<name>.whatsapp.
Local development and testing
- No Meta account: with
notify.whatsapp: local,mainsdk devlogs one line per WhatsApp message with the number masked. - Meta test number: send real messages to the few recipients you register on it, with a temporary token, before your own number and templates are approved.
- Tests:
createTestApp()from@mainsdk/testingrunsnotifyon a local channel whennotify.whatsappislocal; read it withchannels(app).whatsapp. It enforces consent, opt-outs and quiet hours.channels(app).whatsapp?.failNext(1)simulates an outage, andfakeMessagingServer("whatsapp-cloud")records the request the real driver makes.
What MainSDK adds on top
- WhatsApp only to recipients with recorded consent; without it the channel is skipped with
MAIN-NOTIFY-0004and the next channel is tried. - Business-initiated messages only with provider-approved templates.
- Opt-outs and quiet hours respected, in the person's time zone.
- Sign-in codes single use, expiring after 5 minutes, closed after 5 wrong tries, and rate limited per phone and per IP.
- A retry with the same idempotency key never sends twice.
See Notifications and Sign-in and sessions.
Limits and notes
- The WhatsApp channel driver is built (spec
email-notify). Theauth.whatsappsender comes with the auth spec. - The driver declares its own host,
graph.facebook.com, sonetwork.allowdoes not change. - Set
notify.limits.whatsapp-per-dayto bound cost; more is skipped withMAIN-NOTIFY-0010. - Meta limits how many people a new number can message in a day and raises the limit as quality holds. Plan rollouts with
notify.rolloutstages. - Phone numbers are personal data. Label the fields that store them
personalindb. - Consent records are kept in your app's
kvstore, per phone number, as a keyed hash. Record consent withnotify.consent.create(user, "whatsapp").
Swap to or from it
notify:
whatsapp: bird # was whatsapp-cloud
auth:
whatsapp: { driver: bird } # was whatsapp-cloudYour notify() and auth.whatsapp calls stay the same, and the WhatsApp conformance cases pass for WhatsApp Cloud API, Bird and MSG91. Add the notify.bird block with it (see Bird).
FAQ
Can I send any text on WhatsApp? Not as the first message. Business-initiated messages must use an approved template, and MainSDK sends only to people with recorded consent.
Do I need a separate setup for sign-in codes? The same WhatsApp Business Account works for both. Sign-in codes need an approved template in the authentication category.
WhatsApp Cloud API or Bird or MSG91? All three pass the same conformance cases. Use the Cloud API to deal with Meta directly; use Bird or MSG91 if you already send SMS through them.
Next
Was this page helpful?