WhatsApp authentication templates
An authentication template delivers a one-time passcode. Meta supplies its wording when you author the template. When you send it, provide the code as a body parameter.
Before you send
Choose whether to use a Bird-managed template or author a template on your own business account.
Sending Bird's stocked catalog templates, bird_otp and bird_otp_authifly, needs no verification of yours. Those templates live on Bird's own WhatsApp Business Accounts, and the managed send path never checks your business's verification status.
When the connected business account reports not_verified, Bird refuses authentication-template creation or duplication with 412 E15043 WhatsAppTemplateBusinessNotVerified. Check the connected account and its latest synchronized status. See WhatsApp business verification for the verification process and status handling.
Utility and marketing template authoring is unaffected by this gate; you can keep creating and editing those regardless of your verification status.
Author templates in the dashboard, with the bird CLI, or through the MCP server. See Authoring WhatsApp templates for the full flow.
Sending a passcode
POST /v1/whatsapp/messages with a template object naming a catalog slug:
const msg = await bird.whatsapp.send({
to: "+14155550100",
template: {
slug: "bird_otp",
language: "en",
components: [{ type: "body", parameters: [{ type: "text", text: "481920" }] }],
},
});
console.log(msg.id, msg.status);msg = client.whatsapp.send(
to="+14155550100",
template="bird_otp",
language="en",
components=[{"type": "body", "parameters": [{"type": "text", "text": "481920"}]}],
)
print(msg.id, msg.status)package main
import (
"context"
"fmt"
"log"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
code := "481920"
components := []bird.WhatsAppMessageTemplateComponent{{
Type: "body",
Parameters: &[]bird.WhatsAppMessageTemplateComponentParameter{{Type: "text", Text: &code}},
}}
msg, err := client.Whatsapp.Send(context.Background(), bird.WhatsappSendParams{
To: "+14155550100",
Template: "bird_otp",
Language: "en",
Components: components,
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)
}$components = [
(new WhatsAppMessageTemplateComponent())
->setType('body')
->setParameters([
(new WhatsAppMessageTemplateComponentParameter())->setType('text')->setText('481920'),
]),
];
$message = $bird->whatsapp->send(
to: '+14155550100',
template: 'bird_otp',
language: 'en',
components: $components,
);
echo $message->getId(), ' ', $message->getStatus();bird whatsapp send \
--components '[{"parameters":[{"text":"481920","type":"text"}],"type":"body"}]' \
--language en \
--template bird_otp \
--to +14155550100curl -X POST "https://us1.platform.bird.com/v1/whatsapp/messages" \
-H "Authorization: Bearer $BIRD_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+14155550100",
"template": {
"slug": "bird_otp",
"language": "en",
"components": [
{
"type": "body",
"parameters": [
{
"type": "text",
"text": "481920"
}
]
}
]
}
}'Four rules are specific to this category:
- Omit from. A Bird-managed template picks its own sender, from the category and region, so setting from returns 422 E15018 WhatsAppSenderNotAllowed. That's the opposite of a free-form send, which requires from, worth remembering if you're arriving here from the interactive-message pages.
- to must be an E.164 phone number. An authentication template can't go to a business-scoped user ID; that's 422 E15014 WhatsAppRecipientNotSupportedForTemplate.
- The body takes exactly one positional parameter, the code. Zero parameters, or naming the parameter, returns 422 E15003 WhatsAppTemplateParameterMismatch. Authentication is the one category Meta writes positionally; every other category names its parameters.
- No customer service window is needed. Template sends aren't window-gated, which is the whole reason a passcode template exists: it has to reach someone who has never messaged you before.
Read the available languages for bird_otp or bird_otp_authifly from the template catalog. If the language you request is unavailable, the send fails rather than substituting another language.
The copy code button
Meta writes an authentication template's body itself, as a preset carrying a single code placeholder, so you supply flags rather than text. The button component is optional in a send: if you don't include one, Bird adds it for you, carrying the same code as the body. You can also supply it yourself:
Code example
{ "type": "button", "parameters": [{ "type": "text", "text": "481920" }] }Either way, exactly one button reaches WhatsApp, and it's the copy-code button: tapping it copies the code to the clipboard. Bird supports copy_code only; the other two button behaviors Meta documents for authentication templates, one-tap and zero-tap autofill, aren't available on Bird today.
Authoring a template's button follows the same shape: one otp button, and the template takes no other button type. You supply add_security_recommendation (a boolean shown in the body) and code_expiration_minutes (1 to 90, shown in the footer) instead of writing copy.
What Meta allows in an authentication template
Meta fixes an authentication template's structure and reviews its content: no URLs, media, or emojis anywhere in the template, and a 15-character cap on the code parameter. The category also changes how WhatsApp delivers the message, sending it only to the recipient's primary device. See Template guidelines for the fixed structure, character limits, and review process in full.
Cost
Category and destination set the price. See WhatsApp authentication-international rates for how sending to a country other than your primary location can change it, and Cost and billing for when a send is charged. Rate numbers live on WhatsApp pricing.
Things to watch
- Bird's stocked templates do not deliver to nine countries. bird_otp and bird_otp_authifly send from Bird's own WhatsApp Business Accounts, and those accounts do not carry authentication messages to Egypt, India, Indonesia, Malaysia, Nigeria, Pakistan, Saudi Arabia, South Africa, or the United Arab Emirates. Such a send is refused 422 E15063 WhatsAppDestinationRestricted before anything is charged. A template you authored on your own account, sent from your own number, reaches these countries normally. Verify reaches them too, by moving the passcode to another channel on its own.
- An authored send requires from, and it has to sit on the same WhatsApp Business Account as the template. A sender on a different account is refused 422 E15023 WhatsAppSenderWABAMismatch before anything is charged.
- Only a language whose version is approved and live is sendable. A draft, pending, rejected, or paused language isn't.
- Meta can recategorize a template on its own initiative. There's no opt-out, and it moves the pricing and delivery rules that follow the category.
- The template's category and its language's category can diverge. See WhatsApp templates for how the send path resolves this.
- An ingested authentication template can't be duplicated. Bird can't read WhatsApp's generated copy back into the settings a new template is built from; that's 422 E15024 WhatsAppTemplateContentNotDuplicable. Create a new one with its own security recommendation and code expiration instead.
Next steps
- WhatsApp templates: browsing the catalog and the shared send-by-template contract
- Utility templates: order updates, appointment reminders, and account notices
- WhatsApp business verification: how verification works and what else it unlocks
Related resources
Continue with the documentation, guides and examples for this topic. Resources are in English.
Watch the guideConnecting WhatsApp to Bird: from buying a number to a live channelUnderstand the conceptWhat is the 24-hour customer service window on WhatsApp?Use the toolWhatsApp message builderExplore the capabilityWhatsApp
Try the practice and get an implementation brief