Two credentials, and they are different things
Confusing these is the most common setup mistake, so start here. One lets you send; the other lets you trust what comes back.
- Access token (
META_ACCESS_TOKEN) authorises sending messages as your number. - App secret (
META_APP_SECRET) verifies that inbound delivery callbacks really came from Meta. Without it, anyone who learns your webhook URL can forge delivery statuses.
This project refuses to start in production when WhatsApp credentials are configured and the app secret is missing, rather than warn and continue. A silent downgrade is worse than no boot.
What Meta requires
Meta’s own interface moves around, so this describes the requirements rather than click paths. For the current UI, follow Meta’s Cloud API documentation.
A verified business
Sending production WhatsApp traffic needs a Meta Business account with completed verification: certified documents matching your legal entity, and an international card that supports recurring auto-debit. Indian domestic debit and RuPay cards commonly fail here because of RBI e-mandate rules.
A phone number that is not on WhatsApp
The number you register must not be active on the WhatsApp consumer app. If it is, it has to be deleted from WhatsApp first, and that is one-way. Use a dedicated SIM. There is no way around this, and any guide suggesting otherwise is misdescribing how Meta works.
An authentication template
This is the message your users actually receive. It must be category authentication, and it needs two things:
- a body containing one variable: the code
- a Copy Code button bound to that same variable
The code is sent in both places, because that is what an authentication template requires in order to render its button. Then set the template’s technical name, not its display name:
META_TEMPLATE=your_template_name META_TEMPLATE_LANG=en_US
If you edit the template later and remove the button, delivery starts failing. That is worth writing down somewhere your future self will look.
A permanent token
The token shown in the API setup panel expires in about a day and is for testing. For a real install, create a system user with the whatsapp_business_messaging permission and generate a token for it, so it does not expire out from under you.
A webhook that verifies signatures
Your callback URL is derived from your configured app URL:
{APP_URL}/webhooks/whatsappMeta calls it once with a challenge during subscription, then posts delivery statuses to the same URL. Set a verify token you invent, subscribe to the messages field, and set the app secret so every callback is authenticated by its X-Hub-Signature-256 header.
Testing before you have a verified business
Meta’s test number is a genuinely useful sandbox: it delivers only to recipients you allow-list, which is exactly what you want while wiring a flow. Two things to know:
- Allow-list every recipient. A send to a number that is not on the list is rejected by Meta and surfaces here as
502 delivery_failedwithretryable: false. That flag is deliberate: retrying will not help. - The sample template has a different shape.It takes three body parameters instead of one. If you are sending with Meta’s sample template rather than your own, name it in
META_SANDBOX_TEMPLATEand the gateway uses the matching payload shape. Leave it unset for any real install.
The constraints that shape your product
These are Meta’s rules. Each one has ended someone’s launch, and none can be fixed in application code.
- Business-initiated messages need a template. Free-form text is only deliverable inside an open 24-hour customer service window. OTP sends are business-initiated, which is why the gateway always uses a template.
- Messaging limits are tiered. A new number starts on a low daily unique-recipient limit and climbs as its quality rating stays healthy. Plan a launch around this rather than discovering it during one.
- Meta bills you directly. See Meta’s pricing. This project does not resell messaging, bundle credits, or sit between you and that bill.
- A bad quality rating throttles or blocks you. Authentication-category traffic with a real user base is normally safe. Marketing-shaped content is what gets numbers flagged.
Or skip all of it
Every requirement above is a WhatsApp requirement. Telegram needs one bot token, no business verification, no card and no approval process, and delivery is free. If you are building an internal tool, a side project, or anything that can ask its users to tap a link once, the Telegram channel is strictly easier and costs nothing.
You can run both. The API takes a channel field, quota is metered against WhatsApp only, and moving traffic to Telegram is the documented way around a spent monthly cap.
Next
- Self-hosting: where these values go, and what production refuses to boot without.
- API reference: the
502 retryableflag and every other error. - Try it in the browser: watch the provider rejection happen without waiting for a template approval.