Skip to main content

Create Template

Create a WhatsApp template and submit it to Meta for review in one call. WhatsApp only lets a business start a conversation with an approved template.

Approval is asynchronous. The template is returned with status: "pending". Poll Get Template until it is approved or rejected; review usually takes a few minutes and can take up to 48 hours.

Endpoint​

POST /whatsapp/templates

Returns: 201 Created

Your organization needs a connected WhatsApp Business account, set up in the dashboard under Integration > WhatsApp.

Headers​

HeaderValueRequired
AuthorizationBearer <your-api-key>Yes
Content-Typeapplication/jsonYes

Body Parameters​

ParameterTypeRequiredDescription
namestringYesLowercase letters, digits and underscores, starting with a letter. Max 512 characters. Unique per language.
languagestringYesMeta language code, e.g. en, ar or en_US.
categorystringYesmarketing, utility or authentication.
componentsobject[]For marketing and utilityThe template content. See Components. Not accepted for authentication.
authenticationobjectFor authenticationThe options of a one-time-code template. See Authentication templates.
Which category can I send?

utility and authentication templates are sent with POST /whatsapp/send. A marketing template can be created here, but it is sent as a campaign from the dashboard, which checks marketing consent.

Components​

components uses Meta's own template format and is passed through unchanged, so type, format and button type are written in uppercase exactly as Meta documents them.

ComponentFieldsNotes
HEADERformat, textOptional. format must be TEXT, and text is at most 60 characters. Image, video and document headers are not available through the API yet; create those in the dashboard.
BODYtextRequired, exactly one. At most 1024 characters.
FOOTERtextOptional.
BUTTONSbuttons[]Optional. Each button has a type of QUICK_REPLY, URL or PHONE_NUMBER, a text label, and a url or phone_number where it applies.

Placeholders​

Write placeholders as named variables in lowercase snake_case: {{first_name}}, {{order_id}}. A name starts with a letter, uses only lowercase letters, digits and underscores, and is at most 60 characters. Numbered placeholders such as {{1}} are not accepted, and a name can be used in the header or the body, not both.

The names you choose are the keys you pass in parameters when you send the template. The response lists them in parameters.

Meta requires a sample value for every placeholder when it reviews a template. Arsel fills these in for you. To supply your own, add Meta's example object to the component:

{
"type": "BODY",
"text": "Hi {{first_name}}, order {{order_id}} has shipped.",
"example": {
"body_text_named_params": [
{ "param_name": "first_name", "example": "Sara" },
{ "param_name": "order_id", "example": "A-1029" }
]
}
}

Authentication templates​

Meta fixes the wording of an authentication template, so you choose options instead of writing components. The one-time code is sent as parameters.code.

{
"name": "login_code",
"language": "en",
"category": "authentication",
"authentication": {
"otp_type": "copy_code",
"code_expiration_minutes": 10
}
}
FieldTypeRequiredDescription
otp_typestringYescopy_code, one_tap or zero_tap: how the recipient gets the code off the screen.
add_security_recommendationbooleanNoAppends "For your security, do not share this code." to the body.
code_expiration_minutesnumberNoAdds a footer reading "This code expires in N minutes." 1 to 90.
button_textstringNoButton label, max 25 characters. Meta supplies a localized default.
autofill_textstringNoLabel shown while autofilling. One-tap and zero-tap only. Max 25 characters.
supported_appsobject[]For one_tap and zero_tapUp to 5 Android apps allowed to autofill the code, each { "package_name", "signature_hash" }.
zero_tap_terms_acceptedbooleanFor zero_tapMust be true: accepts Meta's zero-tap terms.
message_send_ttl_secondsnumberNoHow long Meta may keep retrying delivery before dropping the code. 30 to 900.

Response​

{
"id": "01957e3a-4b5c-7d8e-9f0a-1b2c3d4e5f6a",
"name": "order_shipped",
"language": "en",
"category": "utility",
"status": "pending",
"rejection_reason": null,
"parameters": [
"first_name",
"order_id"
],
"components": [
{
"type": "HEADER",
"format": "TEXT",
"text": "Your order"
},
{
"type": "BODY",
"text": "Hi {{first_name}}, order {{order_id}} has shipped."
},
{
"type": "FOOTER",
"text": "Arsel Store"
},
{
"type": "BUTTONS",
"buttons": [
{
"type": "URL",
"text": "Track",
"url": "https://example.com/track"
}
]
}
],
"created_at": "2026-03-08T12:00:00.000Z",
"updated_at": "2026-03-08T12:00:00.000Z"
}

See Get Template for the field reference.

Retries​

A template is identified by its name and language. If you repeat a create that already succeeded, you get a 409; read the template back with GET /whatsapp/templates?name=order_shipped.


Examples​

curl -X POST "https://api.arsel.sa/v1/whatsapp/templates" \
-H "Authorization: Bearer $ARSEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "order_shipped",
"language": "en",
"category": "utility",
"components": [
{
"type": "HEADER",
"format": "TEXT",
"text": "Your order"
},
{
"type": "BODY",
"text": "Hi {{first_name}}, order {{order_id}} has shipped."
},
{
"type": "FOOTER",
"text": "Arsel Store"
},
{
"type": "BUTTONS",
"buttons": [
{
"type": "URL",
"text": "Track",
"url": "https://example.com/track"
}
]
}
]
}'

Error Responses​

{
"status_code": 422,
"name": "validation_error",
"message": "Placeholders must be named in lowercase snake_case, e.g. {{first_name}}. Not accepted: {{firstName}}"
}