Create AUTHENTICATION templates for OTP and verification code delivery using Meta copy-code buttons.
Authentication Template Creation
Summary
Create AUTHENTICATION templates for OTP and verification code delivery using Meta copy-code buttons.
Prerequisites
- Authorization: Bearer w91_live_xxx
- Content-Type: application/json for JSON requests
Related documentation
Authentication templates are for one-time passwords, login verification, account recovery, and transaction confirmation codes. Use Meta copy-code OTP format and keep the template focused on the verification action.
Warning
Do not include URLs, emojis, headers, footers, or non-copy-code buttons in AUTHENTICATION templates. Use one COPY_CODE button with an example code.
Account age eligibility
Authentication template creation is restricted for newly created accounts. By default, the account must be at least 14 days old before AUTHENTICATION templates can be created, or account meta must contain an authenticationTemplateEligibleAt value that is already in the past.
Warning
The API must verify account age from trusted account meta on the server. Any request-supplied account age is ignored, so clients cannot bypass this restriction by sending their own creation date or eligibility flag.
| Account meta field | Required | Notes |
|---|---|---|
| accountMeta.createdAt | Yes | Server-side account creation timestamp used to calculate account age. |
| accountMeta.authenticationTemplateEligibleAt | Optional | Explicit server-side eligibility timestamp. If present, AUTHENTICATION templates are allowed only after this value is in the past. |
| template.metadata.accountAge | No | Ignored. Client-provided age or eligibility values are not trusted. |
Create Authentication Template API
Endpoint: POST /api/v2/templates.
| Header | Required | Description |
|---|---|---|
| Authorization | Yes | Use Authorization: Bearer w91_public_token_here. Compatibility body fields authToken, auth_token, or token are also accepted. |
| Content-Type | Yes | Use application/json. Authentication templates do not use media headers. |
/api/v2/templates| Parameter | Type | Required | Description |
|---|---|---|---|
senderId | string | Optional | WhatsApp sender number. Omit when token scope can resolve the sender. |
template.name | string | Required | Unique authentication template name. |
template.category | string | Required | Must be AUTHENTICATION for this page. |
template.language | string | Required | Language code such as en. |
template.buttons[0].type | string | Required | Use COPY_CODE. |
template.buttons[0].example | string | Required | Example OTP or verification code such as 123456. |
template.metadata | object | Optional | Local Whats91 reference metadata, not submitted to Meta. |
curl -X POST "https://graph.whats91.com/api/v2/templates" \
-H "Authorization: Bearer w91_public_token_here" \
-H "Content-Type: application/json" \
-d '{
"senderId": "916268662275",
"template": {
"name": "login_otp_v1",
"category": "AUTHENTICATION",
"language": "en",
"buttons": [
{ "type": "COPY_CODE", "example": "123456" }
],
"metadata": {
"clientReferenceId": "auth-login-template"
}
}
}'{
"success": true,
"message": "Template submitted to Meta",
"data": {
"templateUid": "0e6fb4f1-5a60-4d1d-8ef2-61a6ec8b7103",
"templateId": "w91_1780300000000_ab12cd34",
"templateName": "login_otp_v1",
"normalizedTemplateName": "login_otp_v1",
"category": "AUTHENTICATION",
"language": "en",
"status": "PENDING",
"senderId": "916268662275",
"phoneNumberId": "1043189608869917",
"wabaId": "643703032991069"
},
"metadata": {
"apiVersion": "v2",
"requestId": "request-uuid",
"processingTimeMs": 320
}
}{
"success": false,
"message": "Authentication templates only support COPY_CODE buttons",
"error_code": "VALIDATION_FAILED",
"details": {
"field": "template.buttons",
"allowedButtonType": "COPY_CODE"
},
"metadata": {
"apiVersion": "v2",
"requestId": "request-uuid"
}
}{
"success": false,
"message": "Authentication template creation is restricted for newly created accounts",
"error_code": "AUTHENTICATION_TEMPLATE_ACCOUNT_AGE_RESTRICTED",
"details": {
"requiredMinimumAgeDays": 14,
"accountAgeDays": 3,
"accountMetaSource": "account meta",
"authenticationTemplateEligibleAt": "2026-06-21T00:00:00.000Z"
},
"metadata": {
"apiVersion": "v2",
"requestId": "request-uuid"
}
}Required fields
| Field | Type | Notes |
|---|---|---|
| template.name | string | Unique name for the OTP or verification template. |
| template.category | string | Use AUTHENTICATION. |
| template.language | string | Supported Meta language code such as en. |
| template.buttons[0].type | string | Use COPY_CODE only. |
| template.buttons[0].example | string | Example code value used for Meta review. |
Optional fields
| Field | Type | Notes |
|---|---|---|
| senderId | string | Explicit sender number for multi-number tokens. |
| template.metadata | object | Local integration reference data stored by Whats91. |
Practical Authentication Examples
1. Login OTP
{
"template": {
"name": "login_otp_v1",
"category": "AUTHENTICATION",
"language": "en",
"buttons": [
{ "type": "COPY_CODE", "example": "123456" }
]
}
}2. Signup verification
{
"template": {
"name": "signup_verification",
"category": "AUTHENTICATION",
"language": "en",
"buttons": [
{ "type": "COPY_CODE", "example": "489201" }
]
}
}3. Password reset code
{
"template": {
"name": "password_reset_code",
"category": "AUTHENTICATION",
"language": "en",
"buttons": [
{ "type": "COPY_CODE", "example": "752904" }
],
"metadata": {
"clientReferenceId": "password-reset-template"
}
}
}4. Transaction PIN code
{
"template": {
"name": "transaction_pin_code",
"category": "AUTHENTICATION",
"language": "en",
"buttons": [
{ "type": "COPY_CODE", "example": "908172" }
]
}
}5. Account recovery code
{
"template": {
"name": "account_recovery_code",
"category": "AUTHENTICATION",
"language": "en",
"buttons": [
{ "type": "COPY_CODE", "example": "650219" }
]
}
}6. Two-step login code
{
"template": {
"name": "two_step_login_code",
"category": "AUTHENTICATION",
"language": "en",
"buttons": [
{ "type": "COPY_CODE", "example": "341875" }
],
"metadata": {
"clientReferenceId": "two-step-login"
}
}
}SDK Examples
Use these examples as starting points for server-side implementations.
curl -X POST "https://graph.whats91.com/api/v2/templates" \
-H "Authorization: Bearer w91_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"senderId": "916268662275",
"template": {
"name": "login_otp_v1",
"category": "AUTHENTICATION",
"language": "en",
"buttons": [
{
"type": "COPY_CODE",
"example": "123456"
}
],
"metadata": {
"clientReferenceId": "auth-login-template"
}
}
}'const response = await fetch("https://graph.whats91.com/api/v2/templates", {
method: "POST",
headers: {
"Authorization": "Bearer w91_live_xxx",
"Content-Type": "application/json"
},
body: JSON.stringify({
"senderId": "916268662275",
"template": {
"name": "login_otp_v1",
"category": "AUTHENTICATION",
"language": "en",
"buttons": [
{
"type": "COPY_CODE",
"example": "123456"
}
],
"metadata": {
"clientReferenceId": "auth-login-template"
}
}
})
});
const data = await response.json();
console.log(data);$ch = curl_init("https://graph.whats91.com/api/v2/templates");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer w91_live_xxx",
"Content-Type: application/json"
],
CURLOPT_POSTFIELDS => json_encode([
"senderId" => "916268662275",
"template" => [
"name" => "login_otp_v1",
"category" => "AUTHENTICATION",
"language" => "en",
"buttons" => [
[
"type" => "COPY_CODE",
"example" => "123456"
]
],
"metadata" => [
"clientReferenceId" => "auth-login-template"
]
]
])
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;import requests
response = requests.request(
"POST",
"https://graph.whats91.com/api/v2/templates",
headers={
"Authorization": "Bearer w91_live_xxx",
"Content-Type": "application/json",
},
json={
"senderId": "916268662275",
"template": {
"name": "login_otp_v1",
"category": "AUTHENTICATION",
"language": "en",
"buttons": [
{
"type": "COPY_CODE",
"example": "123456"
}
],
"metadata": {
"clientReferenceId": "auth-login-template"
}
}
}
)
print(response.json())using System.Text;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer w91_live_xxx");
var request = new HttpRequestMessage(HttpMethod.Post, "https://graph.whats91.com/api/v2/templates");
request.Content = new StringContent(
"""
{
"senderId": "916268662275",
"template": {
"name": "login_otp_v1",
"category": "AUTHENTICATION",
"language": "en",
"buttons": [
{
"type": "COPY_CODE",
"example": "123456"
}
],
"metadata": {
"clientReferenceId": "auth-login-template"
}
}
}
""",
Encoding.UTF8,
"application/json"
);
var response = await client.SendAsync(request);
Console.WriteLine(await response.Content.ReadAsStringAsync());Related APIs
Template
Create MARKETING templates for offers, promotions, launches, and customer engagement campaigns.
Utility
Create UTILITY templates for transactional updates, invoices, reminders, appointments, orders, and service notifications.
Template Send
Send approved WhatsApp templates with Whats91-specific fields, aliases, media headers, and button parameters.
Webhook
Create and manage Whats91 Webhooks v2 event destinations with the canonical public API route.