Skip to content
Whats91

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

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 fieldRequiredNotes
accountMeta.createdAtYesServer-side account creation timestamp used to calculate account age.
accountMeta.authenticationTemplateEligibleAtOptionalExplicit server-side eligibility timestamp. If present, AUTHENTICATION templates are allowed only after this value is in the past.
template.metadata.accountAgeNoIgnored. Client-provided age or eligibility values are not trusted.

Create Authentication Template API

Endpoint: POST /api/v2/templates.

HeaderRequiredDescription
AuthorizationYesUse Authorization: Bearer w91_public_token_here. Compatibility body fields authToken, auth_token, or token are also accepted.
Content-TypeYesUse application/json. Authentication templates do not use media headers.
POST/api/v2/templates
ParameterTypeRequiredDescription
senderIdstringOptionalWhatsApp sender number. Omit when token scope can resolve the sender.
template.namestringRequiredUnique authentication template name.
template.categorystringRequiredMust be AUTHENTICATION for this page.
template.languagestringRequiredLanguage code such as en.
template.buttons[0].typestringRequiredUse COPY_CODE.
template.buttons[0].examplestringRequiredExample OTP or verification code such as 123456.
template.metadataobjectOptionalLocal Whats91 reference metadata, not submitted to Meta.
Authentication create request
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 Response
{
  "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
  }
}
Validation Error Response
{
  "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"
  }
}
Account Age Restriction Error Response
{
  "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

FieldTypeNotes
template.namestringUnique name for the OTP or verification template.
template.categorystringUse AUTHENTICATION.
template.languagestringSupported Meta language code such as en.
template.buttons[0].typestringUse COPY_CODE only.
template.buttons[0].examplestringExample code value used for Meta review.

Optional fields

FieldTypeNotes
senderIdstringExplicit sender number for multi-number tokens.
template.metadataobjectLocal integration reference data stored by Whats91.

Practical Authentication Examples

1. Login OTP

Request body
{
  "template": {
    "name": "login_otp_v1",
    "category": "AUTHENTICATION",
    "language": "en",
    "buttons": [
      { "type": "COPY_CODE", "example": "123456" }
    ]
  }
}

2. Signup verification

Request body
{
  "template": {
    "name": "signup_verification",
    "category": "AUTHENTICATION",
    "language": "en",
    "buttons": [
      { "type": "COPY_CODE", "example": "489201" }
    ]
  }
}

3. Password reset code

Request body
{
  "template": {
    "name": "password_reset_code",
    "category": "AUTHENTICATION",
    "language": "en",
    "buttons": [
      { "type": "COPY_CODE", "example": "752904" }
    ],
    "metadata": {
      "clientReferenceId": "password-reset-template"
    }
  }
}

4. Transaction PIN code

Request body
{
  "template": {
    "name": "transaction_pin_code",
    "category": "AUTHENTICATION",
    "language": "en",
    "buttons": [
      { "type": "COPY_CODE", "example": "908172" }
    ]
  }
}

5. Account recovery code

Request body
{
  "template": {
    "name": "account_recovery_code",
    "category": "AUTHENTICATION",
    "language": "en",
    "buttons": [
      { "type": "COPY_CODE", "example": "650219" }
    ]
  }
}

6. Two-step login code

Request body
{
  "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
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"
    }
  }
}'
Node.js
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);
PHP
$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;
Python
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())
C#
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