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 settingfromreturns422E15018WhatsAppSenderNotAllowed. That's the opposite of a free-form send, which requiresfrom, worth remembering if you're arriving here from the interactive-message pages. tomust be an E.164 phone number. An authentication template can't go to a business-scoped user ID; that's422E15014WhatsAppRecipientNotSupportedForTemplate.- The body takes exactly one positional parameter, the code. Zero parameters, or naming the parameter, returns
422E15003WhatsAppTemplateParameterMismatch. 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:
{ "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_otpandbird_otp_authiflysend 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 refused422E15063WhatsAppDestinationRestrictedbefore 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 refused422E15023WhatsAppSenderWABAMismatchbefore 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
422E15024WhatsAppTemplateContentNotDuplicable. 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.