Send messages with the WhatsApp API: OTP codes, templates and text
Last updated: 11 October 2026 · 9 min read
Both templates and plain text go through one endpoint: POST /v1/accounts/{account_id}/whatsapp/messages. The message is accepted and queued right away, and the response comes back with 201.
Steps
-
1
Find the template
GET /v1/accounts/{account_id}/whatsapp/templatesreturns only APPROVED templates.channel_idis required,nameis optional.Each template has
name,language,category,parameter_format,componentsandexample_parameters.example_parametersis a ready skeleton of the parameters the template expects: copy it and replace the values with your own data. Meta rejects a send with a missing variable.synced_atshows when the templates were last fetched from Meta. For the API to see a new template, click Sync Templates in the dashboard. -
2
Request fields
channel_id(required): the id of the WhatsApp Cloud API inboxphone_number(required): the recipient's number, in international formattext: free text, sent only when the window is opentemplate: a template send, required if there is notext.template.name(the template's exact name) andtemplate.language(az,en,tr,ru,en_USand so on) are required, andtemplate.parametersare the template's variablesfallback_template: used together withtextwhen the window is closedconversation_status:resolved(default),openorpendingcontact.name,contact.email,contact.identifier(the customer's id in your system): optionalsource_reference: an idempotency keystatus_callback: to receive the message's status at your own URL (url,after_seconds,on_change,secret), optional. Details are in the article on tracking a message's delivery status
-
3
Send a verification code (OTP)
Send it with an AUTHENTICATION category template. The request body:
{ "channel_id": 159, "phone_number": "+994 50 123 45 67", "template": { "name": "verification_code", "language": "az", "parameters": { "body": { "1": "483920" } } }, "contact": { "name": "Kenan Rzayev", "identifier": "crm-10422" }, "source_reference": "otp-10422-1754582400" }Keep the code's validity short; 5 minutes is recommended. One OTP message costs 0.036 AZN.
-
4
Send a notification
Send the text together with
fallback_template:{ "channel_id": 159, "phone_number": "+994 50 123 45 67", "text": "Your parcel AZ-10422 is with the courier.", "fallback_template": { "name": "kargo_status", "language": "az", "parameters": { "body": { "1": "AZ-10422", "2": "with the courier" } } } }If the window is open, the text goes out for free (
sent_as: "session"); if it is closed, the template goes out (sent_as: "template"). -
5
Template parameters
Parameters are grouped by template part:
header,body,footer,buttons.If
parameter_formatis POSITIONAL, the keys ofbodyare numbers matching the order of{{1}},{{2}}; if it is NAMED, they are the names you gave in Meta:"body": { "1": "Kenan", "2": "AZ-10422" } "body": { "customer_name": "Kenan", "order_id": "AZ-10422" }If there is only a
body, a short form is also accepted:"parameters": { "1": "483920" }.- If the header is an image, video or document, send
media_url(a public https address) andmedia_type:image(JPG, PNG),video(MP4),document(PDF and others, withmedia_namefor the file name). - Buttons:
url(theparameteris added to the end of the link) andcopy_code(a copy-code button, at most 15 characters). Static link buttons and quick reply buttons need no parameter. - For an amount, the
currencytype (code,amount_1000,fallback_value); for a date,date_time(fallback_value).amount_1000is the amount multiplied by 1000 as a whole number:149000for 149.00 AZN.
- If the header is an image, video or document, send
-
6
Write the number correctly
Write the number with the country code. All of these give the same result; spaces, hyphens, brackets and the “+” are removed automatically:
994501234567 +994 50 123 45 67 (+994) 50-123-45-67
0501234567is wrong: there is no country code, so the message would go to the wrong number. The number must have 8 to 15 digits (E.164), otherwise422comes back. -
7
Read the response
The response comes back as
201 Created:id(the message id),sent_as,status,external_error,whatsapp_message_id,conversation_id,conversation_status,contact_id,channel_id,content,template_name,source_reference,created_at,delivered_at,read_at,status_callback.status: "sent"does not mean delivered; it means Meta has accepted the message.deliveredandreadarrive a few seconds later, so do not tell the customer “delivered” right after sending. -
8
Prevent duplicate sends
When the network drops or a queue runs twice, the same request can be repeated. If you pass
source_reference, a repeated request with the same key within 24 hours does not create a new message and returns the first one: the customer does not get a second code, and Meta does not charge twice. Pick a key that is unique in your system, for exampleotp-{user}-{time}. -
9
Code samples
Section 12 of the document has four samples:
- Node.js: a cargo notification that switches to a template when the window is closed
- PHP: a verification code
- Python: check the window, then send
- C# (.NET): a verification code
In all of them the key is not in the code but in an environment variable (
CLOMNI_TOKEN).