Skip to content
PaalChat Docs

Common workflows

Templates

Find approved templates and fill in their parameters.

.md

In a nutshell

Create templates through the API (Meta reviews them) or in WhatsApp Manager; PaalChat mirrors them. List the APPROVED ones, count the {{n}} placeholders and send one parameter per placeholder.

Where templates come from

A template is a message format Meta has reviewed. Create it through the API (Create template) or in Meta's WhatsApp Manager - PaalChat syncs every template of the account and keeps its status current. A new template is PENDING until Meta decides; the decision arrives as a template.status callback. Delete one language, or all of them, with Delete template.

List them

GET /businesses/{external_id}/whatsapp/templates?status=APPROVED

JSON
{
  "data": [
    {
      "name": "fees_reminder",
      "language": "en_US",
      "category": "UTILITY",
      "status": "APPROVED",
      "rejection_reason": null,
      "components": [
        {"type": "BODY", "text": "Dear {{1}}, the fees balance for {{2}} is GHS {{3}}. Please pay before {{4}}.",
         "example": {"body_text": [["Mrs Mensah", "Kofi", "450.00", "30 October"]]}}
      ],
      "waba_id": "102290129340398",
      "last_synced_at": "2026-09-30T17:54:27+00:00"
    }
  ]
}

Filters: status, name, language. 100 per page. Cache the list and refresh it when a template.status callback arrives.

Fill in the parameters

components are exactly what Meta returns. For each component with {{n}} placeholders, send one parameter per placeholder, in order:

Template component Send
HEADER with {{1}} (text) {"type": "header", "parameters": [{"type": "text", "text": "..."}]}
BODY with {{1}}..{{n}} {"type": "body", "parameters": [{"type": "text", "text": "..."}, ...]}
BUTTONS → URL button with {{1}} {"type": "button", "sub_type": "url", "index": "0", "parameters": [{"type": "text", "text": "..."}]}
No placeholders Send no component for it

For fees_reminder above:

JSON
{
  "to": "+233241234567", "type": "template", "reference": "notification-4411",
  "template": {"name": "fees_reminder", "language": "en_US", "components": [
    {"type": "body", "parameters": [
      {"type": "text", "text": "Mrs Mensah"}, {"type": "text", "text": "Kofi"},
      {"type": "text", "text": "450.00"}, {"type": "text", "text": "30 October"}]}]}
}

Tip

Count placeholders with a regular expression such as /\{\{\d+\}\}/g on each component's text, and validate your parameters before sending. A mismatch fails at Meta with error 132000.

Template status

Status Can send?
APPROVED Yes
PENDING, IN_APPEAL Not yet
REJECTED (see rejection_reason), PAUSED, DISABLED, FLAGGED, LIMIT_EXCEEDED, ARCHIVED, DELETED No - 422 template_not_approved

When a template is rejected or paused, stop offering it. The customer edits it in WhatsApp Manager, and an APPROVED callback follows when Meta approves.

Versions and history

Every change to a template's content is kept as a version - List template versions shows what each said and Meta's decision on it. A template deleted at Meta (through the API or WhatsApp Manager) is kept with status REMOVED; list it with include_removed=1. Nothing is ever deleted from the history.

Checked before sending

PaalChat checks a send against the template before Meta sees it: every body and header variable needs a value (named templates: parameter_name), a media header needs its image, video or document, and a URL button with a variable needs its value. Values may not contain new lines, tabs or more than four spaces in a row. Otherwise the send is refused with 422 invalid_template_parameters, listing what is wrong.

Starter templates

The template library has ready-made templates for schools (fee reminder, results, PTA meeting, absence, admission offer) and shops (order confirmed, receipt, delivery update). Create template from library copies one with the business's name and submits it to Meta.