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

GEThttps://api.mobilesasa.com/api/v1/whatsapp/accounts/{uuid}/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"
Response
{
  "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

POSThttps://api.mobilesasa.com/api/v1/whatsapp/accounts/{uuid}/templates

Parameters

FieldTypeRequiredDescription
display_namestringYesThe name you will recognise, e.g. "Appointment reminder". The Meta name is derived from it (lowercase, underscores).
namestringNoAn explicit Meta name instead: lowercase letters, digits and underscores only.
categorystringYesOne of UTILITY (updates about something the person already has), MARKETING (offers, news) or AUTHENTICATION (one-time passcodes). Meta may reclassify a template at review.
languagestringYesA Meta language code: en, en_GB, sw…
body_textstringYesUp to 1,024 characters. Blanks are numbered {{1}}, {{2}}… in order, and the body may not start or end with one.
body_examplesstring[]NoOne realistic sample per blank. Required when the body has blanks; reviewers read these.
headerobjectNo{ "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_textstringNoSmall print under the body, 60 characters.
buttonsobject[]NoUp 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_recommendationboolNoAuthentication only: adds Meta's "do not share this code" line.
code_expiration_minutesintNoAuthentication 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

Reviewers reject vague or misleading templates. Say what the message is for, give samples that look like real data, and keep the category honest: a discount is marketing even if it mentions an order. Common rejection reasons come back in 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

Meta writes the body of a passcode template itself. Send 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

POSThttps://api.mobilesasa.com/api/v1/whatsapp/accounts/{uuid}/templates/sample

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
curl -X POST https://api.mobilesasa.com/api/v1/whatsapp/accounts/ACCOUNT_UUID/templates/sample \
  -H "Authorization: Bearer mbs_…" \
  -F "[email protected]"
Response
{ "success": true, "data": { "handle": "4::aW1hZ2UvcG5n…" } }

Edit a template

PUThttps://api.mobilesasa.com/api/v1/whatsapp/accounts/{uuid}/templates/{template_uuid}

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

DELETEhttps://api.mobilesasa.com/api/v1/whatsapp/accounts/{uuid}/templates/{template_uuid}
# 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

POSThttps://api.mobilesasa.com/api/v1/whatsapp/accounts/{uuid}/templates/sync

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.