WhatsApp Authentication Messages: Sending OTPs That Arrive
Authentication templates have a fixed structure, a fixed category and a delivery path most teams never test. Here is how to send OTPs on WhatsApp without losing codes.


Authentication templates have a fixed structure, a fixed category and a delivery path most teams never test. Here is how to send OTPs on WhatsApp without losing codes. In this deep dive, we explore actionable strategies, real-world engineering blueprints, and future-proof patterns to elevate your digital operations.
- Authentication is a separate template category, not a styling choice
- Pick the button before you write the code
- The delivery failures nobody tests for
- What it costs, and why the numbers usually improve
- The server-side checklist that keeps codes safe
- Mistakes that get the template rejected
- Where this sits in BNex
- Related Reading
The first WhatsApp OTP integration I built looked finished on the day it shipped. Code generated, template approved, message sent, user logs in. Then support started getting tickets I could not explain: some users waited, some typed the code from an older message, and one person pasted a code that had already expired. The API had returned 200 every time. Nothing was broken in the way that logs usually show.
Authentication is the least forgiving message you will ever send. A marketing message that arrives late is annoying. A login code that arrives late is a lost customer, and they do not tell you, they just close the app.
Authentication is a separate template category, not a styling choice
If your app offers one-time passwords over WhatsApp, Meta requires an authentication template. Not a utility template with a code-shaped variable in it. Not a marketing template that happens to say "verify". The category is part of the template itself and it decides how the message is structured, how it is reviewed and how it is billed.
That matters practically because authentication templates are the only category where Meta controls the wording:
- Fixed preset text:
<code> is your verification code. - An optional security disclaimer: For your security, do not share this code.
- An optional expiry line: This code expires in N minutes.
- Exactly one button type, or no button at all with zero-tap.
You cannot add a promo line, a logo in the body, or your own greeting. Teams complain about this and then quietly do the right thing, because the fixed format is also why these messages are reviewed quickly. The reviewer has almost nothing to judge.
Pick the button before you write the code
Three options exist, and the choice is a product decision, not a developer preference.
| Button | Behaviour | Where it works |
|---|---|---|
| Copy code | Copies the code to the clipboard | Every device |
| One-tap autofill | Opens your app with the code already delivered | Android, needs app-side integration |
| Zero-tap | Broadcasts the code, your app captures it | Android only |
Copy code is the safe default. One-tap is the better experience if you ship an Android app and are willing to register your app package and signature hash on the template. Zero-tap removes even the tap, but it only exists on Android, so it can never be your only path.
On iOS the operating system handles this itself now: from iOS 26, keyboard suggestions pick the code out of the push notification with no integration on your side. Older iOS versions do not, which is exactly why copy code stays in every template I build.
The delivery failures nobody tests for
This is the part that costs teams real users.
Only the primary device gets the code. Meta masks authentication messages on linked devices, so the code is deliberately not shown on a user's second device. If your QA engineer tests with WhatsApp Web open, they will see a masked message and conclude the integration is broken.
Sent is not delivered, and only delivered is billed. Meta charges per delivered message and does not charge for sends that never land. So a silent failure costs you nothing in billing and costs your user everything. Read the status webhooks, not the send response, if you want to know what actually reached the phone.
Nothing retries for you. If a code never arrives, your user taps "resend" or leaves. Build the fallback yourself: if the status webhook does not report delivered within a short window, fall back to SMS or email and say so in the UI. The fallback is not a sign the WhatsApp path failed, it is what makes the WhatsApp path usable.
Rate limits exist at both ends. Your own server must throttle code requests per number and per IP, or a scripted attacker will burn your budget and spam strangers. The platform side has its own throughput and quality limits, covered in how WhatsApp messaging limits actually work.
What it costs, and why the numbers usually improve
Authentication messages sit in their own pricing category, charged per delivered message with rates set per market, and authentication volume tiers exist that reduce the per-message rate as volume grows. I will not quote a number here, because rates change and a stale figure is worse than none. On the Cloud API the rates are published per market and per category, and I walk through the structure in WhatsApp Cloud API pricing in 2026.
The reason teams switch from SMS is rarely the headline rate. It is that WhatsApp verification gets read, the code is in the same app the customer already trusts, and failed deliveries are visible in a status webhook instead of a support queue. If your OTP completion rate is the number you care about, measure it before and after rather than trusting either vendor's pitch.
The server-side checklist that keeps codes safe
Almost all of the security work is on your side. A code is a credential and should be treated like one.
- Store a hash of the code, never the code itself, with a short expiry. Fifteen minutes is the usual ceiling.
- Invalidate a code on first successful use, and invalidate the previous one when a new one is issued.
- Throttle per number, per IP and per device. Cap resends, and add a cooldown between them.
- Never log the code in plain text. If your error handler prints the request body, you have just written credentials into your logs.
- Never put the code in the message template as static text. It goes in the variable, and the variable is the only part you control at send time.
- Record the template language you sent, so a user who switches language does not get a code they asked for in a script they did not.
Then the boring test that catches most of this: send a code, let it expire, send another, and use the first one. If the first one still authenticates, your invalidation is not working and no amount of template work will fix it.
Mistakes that get the template rejected
The rejections I see are almost always the same three. Reusing a utility template with a number in it and hoping the reviewer reads it as a code. Adding a "welcome to our service" line to an authentication template, which changes the category and the intent. And running authentication on a number that also sends marketing, then wondering why code delivery quality dropped after a broadcast. The general rules for getting a template through review are in why WhatsApp templates get rejected, and the step-by-step approval flow is in the guide on WhatsApp message template approval.
One more: do not send an authentication template outside the moment of authentication. It is not a re-engagement message. Using it that way is the fastest route to a quality rating that throttles everything else you send.
Where this sits in BNex
If you are wiring OTPs into a product, BNex sends them through the same WhatsApp Business Platform account you already use for support and notifications: authentication templates are created and sent from the same place as the rest of your messaging, so a code and the conversation it belongs to stay together. The API and template setup do not change, and the delivery statuses land in one inbox instead of two dashboards.
If you are mid-integration and something is not arriving, send me the template name and what the status webhook said. I will tell you which of the failure points above it looks like: message me on WhatsApp.
Related Reading
Get more insights like this
Join our list for practical automation, web engineering, and growth strategy — written by builders, for builders.

Manikandan S
Founder & Technical Lead at ZiyncFounder and technical lead at Ziync. I build high-performance websites and web applications for startups and mid-market teams: React, Next.js, Firebase. Based in Chennai, working with clients worldwide.