Templates
Business-initiated WhatsApp messages must use templates reviewed by Meta. Write them here, submit them for review and send once approved; no Meta Business Manager needed.
Which uuid goes in the path
{uuid} is the WhatsApp number's id, not your team's or your user's. Find it in the portal under WhatsApp, My numbers ("Account ID" on each number) or at the top of WhatsApp, Templates, or from GET /api/v1/whatsapp/accounts. On the shared Mobile Sasa number, use that number's id: your templates are kept separate from other teams'. Each template card in the portal shows its own "Template ID", the template_uuid you send with.List an account's templates
# Paste your token once (it starts with mbs_):
export MOBILESASA_TOKEN="mbs_your_token_here"
curl "https://api.mobilesasa.com/api/v1/whatsapp/accounts/ACCOUNT_UUID/templates" \
-H "Authorization: Bearer $MOBILESASA_TOKEN"{
"success": true,
"data": {
"templates": [
{
"uuid": "b1f4…",
"name": "t1234_statement_ready",
"display_name": "Statement ready",
"language": "en",
"category": "UTILITY",
"status": "APPROVED",
"header_type": "TEXT",
"header_text": "Your statement",
"body_text": "Hi {{1}}, your {{2}} statement is ready. Balance: {{3}}.",
"body_examples": ["Jane", "February", "KES 128,400"],
"placeholders": 3,
"footer_text": "Reply STOP to opt out",
"buttons": [{ "type": "url", "text": "View statement", "url": "https://acme.co.ke/statements" }],
"mine": true,
"submitted_at": "2026-10-01T08:12:00Z",
"created_at": "2026-10-01T08:12:00Z",
"updated_at": "2026-10-01T09:40:00Z"
},
{
"uuid": "0d92…",
"name": "t1234_march_offer",
"display_name": "March offer",
"language": "en",
"category": "MARKETING",
"status": "REJECTED",
"rejected_reason": "INVALID_FORMAT",
"body_text": "…",
"placeholders": 0,
"mine": true
}
]
}
}name is what Meta knows the template as; on the Mobile Sasa number it carries your team prefix. display_name is the name you gave it. status is Meta's review status: PENDING, APPROVED, REJECTED, PAUSED or DISABLED. On the shared number you only see templates your team created, plus Meta's hello_world sample.
Create a template
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| display_name | string | Yes | The name you will recognise, e.g. "Appointment reminder". The Meta name is derived from it (lowercase, underscores). |
| name | string | No | An explicit Meta name instead: lowercase letters, digits and underscores only. |
| category | string | Yes | One of UTILITY (updates about something the person already has), MARKETING (offers, news) or AUTHENTICATION (one-time passcodes). Meta may reclassify a template at review. |
| language | string | Yes | A Meta language code: en, en_GB, sw… |
| body_text | string | Yes | Up to 1,024 characters. Blanks are numbered {{1}}, {{2}}… in order, and the body may not start or end with one. |
| body_examples | string[] | No | One realistic sample per blank. Required when the body has blanks; reviewers read these. |
| header | object | No | { "type": "text", "text": "…", "example": "…" } for a one-line title (60 characters, at most one blank), or { "type": "image" | "video" | "document", "handle": "…" } with a sample handle from the upload call below. Omit or { "type": "none" } for no header. |
| footer_text | string | No | Small print under the body, 60 characters. |
| buttons | object[] | No | Up to three. { "type": "quick_reply", "text": "Yes" }, { "type": "url", "text": "Open", "url": "https://…" } (a trailing {{1}} makes it dynamic; add example) or { "type": "phone_number", "text": "Call us", "phone_number": "+254…" }. |
| add_security_recommendation | bool | No | Authentication only: adds Meta's "do not share this code" line. |
| code_expiration_minutes | int | No | Authentication only: adds "this code expires in N minutes" to the footer (1 to 90). |
# Paste your token once (it starts with mbs_):
export MOBILESASA_TOKEN="mbs_your_token_here"
curl -X POST https://api.mobilesasa.com/api/v1/whatsapp/accounts/ACCOUNT_UUID/templates \
-H "Authorization: Bearer $MOBILESASA_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "Statement ready",
"category": "UTILITY",
"language": "en",
"header": {
"type": "text",
"text": "Your statement"
},
"body_text": "Hi {{1}}, your {{2}} statement is ready. Balance: {{3}}.",
"body_examples": [
"Jane",
"February",
"KES 128,400"
],
"footer_text": "Reply STOP to opt out",
"buttons": [
{
"type": "url",
"text": "View statement",
"url": "https://acme.co.ke/statements"
}
]
}'The template is submitted to Meta straight away and comes back with status: "PENDING". Meta's decision arrives on our webhook and is also re-checked every few minutes, so polling the list endpoint shows the result without any action from you.
Getting approved first time
rejected_reason: INVALID_FORMAT (blanks out of order or at the edges), TAG_CONTENT_MISMATCH (wrong category) and ABUSIVE_CONTENT (policy).Authentication templates are fixed-shape
category: "AUTHENTICATION" with no body_text, a { "type": "copy_code", "text": "Copy code" } button if you want one, and the two options above. At send time the code is params[0].Upload a sample for an image, video or PDF header
A multipart form with one file field. The response carries a handle to place in header.handle when creating the template. Meta only uses it to review the look of the template; the real media goes with each send. Needs the account's app_id on your own Meta app; the Mobile Sasa number has it already.
curl -X POST https://api.mobilesasa.com/api/v1/whatsapp/accounts/ACCOUNT_UUID/templates/sample \
-H "Authorization: Bearer mbs_…" \
-F "[email protected]"{ "success": true, "data": { "handle": "4::aW1hZ2UvcG5n…" } }Edit a template
Same fields as create except category and language, which Meta does not allow to change. An approved template goes back to PENDING until the new wording is reviewed; sends in the meantime fail with a validation error. Meta allows an edit roughly once a day per template.
# Paste your token once (it starts with mbs_):
export MOBILESASA_TOKEN="mbs_your_token_here"
curl -X PUT https://api.mobilesasa.com/api/v1/whatsapp/accounts/ACCOUNT_UUID/templates/TEMPLATE_UUID \
-H "Authorization: Bearer $MOBILESASA_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "Statement ready",
"body_text": "Hi {{1}}, your {{2}} statement is ready. Closing balance: {{3}}.",
"body_examples": [
"Jane",
"February",
"KES 128,400"
]
}'Delete a template
# Paste your token once (it starts with mbs_):
export MOBILESASA_TOKEN="mbs_your_token_here"
curl -X DELETE "https://api.mobilesasa.com/api/v1/whatsapp/accounts/ACCOUNT_UUID/templates/TEMPLATE_UUID" \
-H "Authorization: Bearer $MOBILESASA_TOKEN"Removes it at Meta and here. Messages already sent with it keep their history. Meta blocks the name for 30 days after deletion.
Check with Meta now
Statuses refresh on their own. Call this when you want Meta's answer immediately, or after changing something in Meta Business Manager directly on your own app.
Categories and pricing
On a managed number the template's category sets the price per message: utility and authentication at the lower rate, marketing at the higher. On your own Meta app every message costs the flat platform fee and Meta bills the conversation to your Meta account. Live figures: GET /api/v1/whatsapp/pricing.