Browse guides

Authentication Templates (One-Time Passwords)

How to send one-time passwords (OTP) and verification codes on WhatsApp with authentication templates, the only template category allowed for identity-verification codes. Covers the fixed text ("123456 is your verification code."), the optional security disclaimer and expiry warning (1-90 minutes), and the three delivery options: copy code button (works everywhere), one-tap autofill button (Android, needs a handshake from your app) and zero-tap (Android, code goes straight into your app); plus iOS 26+ keyboard suggestions that autofill numeric 3-8 digit codes. Rules: no URLs, media or emojis, code up to 15 characters, sent twice in the payload (body and button); default time-to-live 10 minutes, settable 30 seconds to 15 minutes; codes go only to the user's primary device; forwarding disabled.

On this page

If you want to send customers a one-time password (OTP) or verification code on WhatsApp, for a login, signup, password reset or payment check, you must use an authentication template. Meta says only authentication templates can send a one-time passcode for identity verification. Marketing and utility templates cannot.

Authentication templates are the most restricted category. You don't write the message text yourself. WhatsApp uses a fixed sentence, and you choose a few options:

  • Fixed text (not customizable): "123456 is your verification code." (with your code in place of 123456)
  • Optional security disclaimer: "For your security, do not share this code."
  • Optional expiry warning: "This code expires in 10 minutes." (you choose the number, from 1 to 90 minutes)
  • A button: a copy code button, a one-tap autofill button, or no visible button when you use zero-tap.

Choosing how the code reaches your app#

OptionWhat the customer doesDevicesNeeds app changes?
Copy codeTaps Copy code, switches to your app, pastes the codeAllNo
One-tap autofillTaps Autofill. WhatsApp opens your app and hands it the code.Android onlyYes: your Android app must start a "handshake" before you send
Zero-tapNothing. The code arrives in your app automatically.Android onlyYes: handshake plus a broadcast receiver in your app
iOS keyboard suggestionsTaps the code suggested above the keyboardiOS 26 and laterNo: on by default for all authentication templates

Meta calls one-tap autofill the preferred option on Android, because the customer never has to leave your app. On a non-Android device, a one-tap or zero-tap template shows a copy code button instead, so the code always gets through.

Security built into authentication templates#

  • Primary device only: authentication messages are delivered only to the user's primary WhatsApp device. On linked devices (such as WhatsApp Web or desktop) the message is masked with a prompt to view it on the primary device. This is on by default, can't be turned off, and is available only on Cloud API.
  • No forwarding: WhatsApp disables forwarding of authentication messages, to fight phishing.
  • Content restrictions: URLs, media and emojis are not allowed in authentication template content or parameters, and parameters are limited to 15 characters.

Best practices from Meta#

  1. Get opt-in and let the user choose WhatsApp

    You need opt-in before messaging someone. A common approach is to offer a choice of channels (WhatsApp, SMS, email). Make it clear the code will arrive on their WhatsApp number. See Opt-in rules.

  2. Confirm their WhatsApp number

    Ask the user to confirm the number before sending. People may use a different number for WhatsApp than for SMS (for example, travellers with two SIM cards). If many codes are undeliverable, users may be picking WhatsApp without being registered on it. On Android you can check that WhatsApp is installed before offering it.

  3. Set a short time-to-live

    Set the template's time-to-live to no more than your code's expiry time, so nobody receives a code that has already expired (see Time-to-live below).

  4. Tell the user the code was received

    When the user pastes the code, or your app receives it through one-tap or zero-tap, show them clearly that your app has it.

  5. Be careful with account recovery

    WhatsApp only verifies phone-number ownership at registration, and numbers get recycled. For sensitive flows such as account recovery, where the WhatsApp code may be the only check, verify the WhatsApp account first (for example with an initial OTP while the user is logged in) or add an extra challenge.

Technical details#

Create a copy code template#

POST /<WHATSAPP_BUSINESS_ACCOUNT_ID>/message_templates with "category": "authentication". You don't send body text, because the text is fixed:

json
{
  "name": "authentication_code_copy_code_button",
  "language": "en_US",
  "category": "authentication",
  "message_send_ttl_seconds": 60,
  "components": [
    { "type": "body", "add_security_recommendation": true },
    { "type": "footer", "code_expiration_minutes": 5 },
    {
      "type": "buttons",
      "buttons": [
        { "type": "otp", "otp_type": "copy_code", "text": "Copy Code" }
      ]
    }
  ]
}
FieldMeaning
add_security_recommendationtrue adds "For your security, do not share this code."
code_expiration_minutesShows "This code expires in N minutes." Minimum 1, maximum 90. Omit to hide the warning.
otp_typecopy_code, one_tap or zero_tap
textOptional button label, up to 25 characters. Defaults to a localized label such as "Copy Code".
message_send_ttl_secondsOptional time-to-live, in seconds

You create the button with "type": "otp", but WhatsApp stores it as a url button. You'll see URL when you read the template back.

One-tap autofill and zero-tap (Android)#

These need your Android app details on the button:

json
{
  "type": "otp",
  "otp_type": "one_tap",
  "text": "Copy Code",
  "autofill_text": "Autofill",
  "supported_apps": [
    { "package_name": "com.example.luckyshrub", "signature_hash": "K8a/AINcGX7" }
  ]
}
  • supported_apps takes up to 5 package name + signing key hash pairs (for different app builds). The signature hash is exactly 11 characters.
  • For zero-tap, use "otp_type": "zero_tap" and add "zero_tap_terms_accepted": true. If it is false, the template is not created. Zero-tap templates still define the one-tap and copy code labels, because they are shown if the automatic delivery fails.
  • Handshake: before calling the API, your app must tell the WhatsApp client to expect a code. If there's no handshake, or the message fails WhatsApp's eligibility checks (for example, the handshake is older than 10 minutes or than code_expiration_minutes, or the package name or signing hash doesn't match), the message shows a copy code button instead.
  • If you set no code_expiration_minutes, a one-tap button is disabled 10 minutes after the message is sent. Otherwise it is disabled after the number of minutes you set.

Meta's OTP Android SDK and sample app cover the handshake and receiver code.

Send the code#

POST /<PHONE_NUMBER_ID>/messages. The code goes in twice: once as the body parameter and once as the button parameter. Maximum 15 characters.

json
{
  "messaging_product": "whatsapp",
  "recipient_type": "individual",
  "to": "12015553931",
  "type": "template",
  "template": {
    "name": "verification_code",
    "language": { "code": "en_US" },
    "components": [
      {
        "type": "body",
        "parameters": [ { "type": "text", "text": "J$FpnYnP" } ]
      },
      {
        "type": "button",
        "sub_type": "url",
        "index": "0",
        "parameters": [ { "type": "text", "text": "J$FpnYnP" } ]
      }
    ]
  }
}

Time-to-live#

If WhatsApp can't deliver a message right away, it keeps trying for the template's time-to-live (TTL), then drops it.

Authentication templates
Default TTL10 minutes (30 days for authentication templates created before October 23, 2024)
Custom rangemessage_send_ttl_seconds from 30 to 900 (30 seconds to 15 minutes), or -1 for 30 days

If you get no delivered status webhook before the TTL runs out, assume the message was dropped.

Creating many languages at once#

POST /<WHATSAPP_BUSINESS_ACCOUNT_ID>/upsert_message_templates creates or updates the same authentication template in several languages in one call. Use "languages": ["en_US","es_ES","fr"] instead of language. An existing template with the same name and language is updated, and the others are created.

"I didn't request a code" button#

Meta is rolling out an "I didn't request a code" button to businesses gradually (beta). When a user taps it, you receive a messages webhook of type: "button" with payload: "DID_NOT_REQUEST_CODE".

Frequently asked#

How do I send an OTP on WhatsApp?

Create an authentication template (category authentication) with a copy code, one-tap or zero-tap button. Once it's approved, send it with the code as both the body parameter and the button parameter. The code can be up to 15 characters.

Can I write my own text for the OTP message?

No. The main text is fixed: "123456 is your verification code.", with your code in place of 123456. You can only add the optional security disclaimer and an expiry warning (1 to 90 minutes), and choose the button. URLs, media and emojis are not allowed.

Can I send OTPs with a utility or marketing template instead?

No. Only authentication templates can be used to send a one-time passcode. Meta also re-checks approved marketing and utility templates. If one should be authentication, it gets rejected and can no longer be sent.

Which button should I use: copy code, one-tap or zero-tap?

Copy code works on every device with no app changes. One-tap autofill is Meta's preferred option on Android: the user taps once and your app gets the code, but your app must do a handshake first. Zero-tap (Android) puts the code straight into your app with no tap at all. On iOS 26 and later, iOS also offers the code above the keyboard automatically.

Why does my autofill button show as Copy code?

The one-tap button falls back to copy code when the user isn't on Android, when your app didn't do a handshake before you sent the message, or when the message fails WhatsApp's eligibility checks. Examples: the handshake is more than 10 minutes old (or older than your expiry setting), or the package name or signing key hash doesn't match your installed app.

Why can't my customer see the code on WhatsApp Web or desktop?

Authentication messages are delivered only to the user's primary WhatsApp device (usually their phone). On linked devices, the message is hidden behind a prompt to view it on the primary device. This can't be turned off.

How long does WhatsApp keep trying to deliver an OTP?

By default 10 minutes for authentication templates. You can set it from 30 seconds to 15 minutes with message_send_ttl_seconds. Meta recommends a TTL no longer than your code's expiry, so people only get codes they can still use.

Does iOS autofill WhatsApp codes?

On iOS 26 and later, yes. iOS detects numeric codes of 3 to 8 digits in the WhatsApp notification and suggests them above the keyboard. Alphanumeric codes aren't supported, and it doesn't trigger while WhatsApp is open. The copy code button is still there as a fallback.

Gotchas & common mistakes#

Still stuck? Open a support ticket and our team will help.