Sign inGet started

List available message templates

GET
/v1/whatsapp/templates
for await (const tpl of bird.whatsapp.templates.list()) {
  console.log(tpl.slug, tpl.status, tpl.available_languages);
}
Response200
{
  "data": [
    {
      "id": "wat_01krdgeqcxet5s7t44vh8rt9mg",
      "slug": "bird_otp",
      "name": "Order update",
      "description": "Sent when an order ships.",
      "scope": "system",
      "waba": "102290129340398",
      "category": "authentication",
      "status": "active",
      "default_language": "pt-BR",
      "on_missing_language": "fallback",
      "language_source_required": false,
      "available_languages": [
        "pt-BR"
      ],
      "languages": {
        "en": {
          "status": "approved"
        }
      },
      "draft_version_id": "wav_01krdgeqcxet5s7t44vh8rt9mg",
      "live_version_id": "wav_01krdgeqcxet5s7t44vh8rt9mg",
      "pending_version_id": "wav_01krdgeqcxet5s7t44vh8rt9mg",
      "last_submitted_at": "2026-07-26T16:40:00Z",
      "next": [
        {
          "kind": "operation"
        }
      ]
    }
  ],
  "next_cursor": "eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE0OjAzOjEwWlwiIiwiaSI6IjAxOTJmM2IxLTRjN2UtN2EyYi05ZDYxLThmM2E1YzJlN2I0MCJ9",
  "prev_cursor": null,
  "refresh_cursor": "eyJ2IjoxLCJzIjoiXCIyMDI2LTA1LTI1VDE2OjQyOjAxWlwiIiwiaSI6IjAxOTJmM2IxLTllMDQtN2NkMy1iODE3LTJhNmY0ZDFjOGUwOSJ9"
}
Returns the WhatsApp message templates available to your workspace: both the workspace's own templates (scope: workspace) and our built-in, Meta-approved templates (scope: system); filter to one tier with scope. Each entry carries the template's slug (the handle you reference when sending), its aggregated status, and the languages a send can currently resolve. It also summarizes where every language stands at Meta, so a list page can show an accurate row without another request. Content is not here: it lives under a version. The list is cursor-paginated. With no scope, the workspace's own templates come first, newest first, followed by our built-in templates.
Query Parameters
waba
string
Filter to a single WhatsApp Business Account by its Meta WABA ID: the same value each template reports in its own waba field. Our built-in templates belong to no account and are never returned when this is set, and an account your workspace does not hold returns an empty page.
status
array
Filter by lifecycle status. Repeat the parameter to match any of several. Our built-in templates are always active.
scope
string
Filter by ownership tier: system for the built-in, Meta-approved template catalog, or workspace for the workspace's own templates. Omit to return both.
Possible values: system, workspace
category
string
Filter by template category.
Possible values (may grow over time): authentication, utility, marketing
q
string
A case-insensitive substring search across the template's slug, name, and description.
limit
integer
Maximum number of items to return per page.
starting_after
string
Cursor from the next_cursor field of a previous list response. Returns items immediately after the cursor position in the current sort order.
ending_before
string
Cursor from the prev_cursor or refresh_cursor field of a previous list response. Returns items immediately before the cursor position in the current sort order. prev_cursor returns the preceding page. refresh_cursor anchors at the first row of that response, which on a newest-first sort is how to fetch the items that have appeared since.
Response Payload
data
array of object
required
Page of templates available to your workspace.
Show child attributes
data.id
string
required
Stable Bird identifier for the template.
data.slug
string
required
The template's handle, editable before the first submission. Address it by this handle, and reference it when sending. Handles beginning with bird_ are reserved for our built-in templates.
data.slug_editable
boolean
required
Whether the slug can still be changed. False after the first submission and for built-in templates.
data.name
string
required
A display name for the template. Nothing resolves through it, so it is safe to show wherever a human reads the template.
data.description
nullable string
required
What the template is for. Null when unset.
data.scope
string
required
Whether the template is one of our built-in templates (system) or one your workspace created (workspace).
Possible values: system, workspace
data.waba
string
The WhatsApp Business Account that holds this template's languages at Meta. Absent on a built-in template: those live on a WABA that Bird manages centrally rather than on your account, so it is not yours to reconcile against and is not disclosed.
data.category
string
required
The category you declared for the template. It is fixed once the template exists. Meta applies its own category per language and may move one, which is what messages are priced at. Read the language for that.
Possible values (may grow over time): authentication, utility, marketing
data.status
string
required
The template's lifecycle, aggregated over its languages.
Possible values: draft, pending, active, rejected, inactive
data.default_language
string
required
The language a send falls back to when on_missing_language is fallback, and the language the template is required to hold.
data.on_missing_language
string
required
What a send does when the language it asks for has no approved copy. Defaults to fail on WhatsApp, because every language is separately approved and separately priced: falling back silently would send content the recipient did not expect at a rate the sender did not choose.
data.language_source_required
boolean
required
When true, a send must name a language explicitly rather than letting the template resolve one.
data.available_languages
array of string
required
The languages a send can resolve right now: approved and not held back by Meta. It shrinks for reasons you did not cause: Meta pauses, disables, archives or limits a language and it leaves the set with nobody having edited anything. Read languages to see which languages exist and why one is missing.
data.languages
object
required
Where each of the template's languages stands, keyed by BCP-47 language tag. This is the summary of the version currently in service, so a template reading active can still hold a rejected or paused language: the aggregate says something is sendable, and this says which. Content is not here; it lives under a version.
data.draft_version_id
nullable string
required
The open draft, or null when nobody is editing. Non-null is the answer to whether this template has unsubmitted work: a draft exists only because someone opened one.
data.live_version_id
nullable string
required
The version Meta is serving. A version goes live as a unit the moment any of its languages is approved, superseding the one before it. Null until a first approval.
data.pending_version_id
nullable string
required
A submitted version still awaiting verdicts: what to poll. It stays set while any language is unresolved, including after a sibling's approval took the version live. Null when nothing is outstanding.
data.last_submitted_at
nullable string
required
When this template was last submitted. Null for a pre-approved built-in template.
data.created_at
nullable string
required
When the template was created. Null for a built-in template, which Bird ships rather than stores.
data.updated_at
nullable string
required
When the template was last modified. Null for a built-in template, which Bird ships rather than stores.
data.next
array of object
What to do next with this template, given the state it is in. Each entry names one action and says why it is worth taking, so you can act on this response without working out the order yourself. Present on reads that compute it: an empty list means there is nothing to do, and the field is absent entirely on responses that do not report next actions.
A draft template routes to opening its draft, a pending one to the version under review, and a rejected or inactive one to a fresh draft. The template's status is the aggregate over its languages, so an entry may send you to the version to see where each language actually stands.
Show child attributes
data.next.kind
string
required
What you do about this step.
  • operation: call the operation named in operation, then read again.
  • external: act somewhere this API does not reach, then read again.
  • wait: nothing is asked of you, so read again later.
  • terminal: nothing you do resolves this, so stop retrying.
Tolerate a value you do not recognize: show the description and offer no action.
Possible values (may grow over time): operation, external, wait, terminal
data.next.description
string
required
A short, human-readable label for the step, suitable for display.
data.next.operation
string
The operationId to call. Present only when kind is operation. The operation's own schema says how to call it; this says only which one, and what to address it with.
data.next.params
object
The parameters that address the operation, by name: {"sender_id": "…"} for an operation on /v1/sms/senders/{sender_id}/requirements. A parameter the operation takes in its query string is given the same way, so an operation addressed as ?subject_id= carries {"subject_id": "…"}. Every parameter the call needs is here, whether its value came from the thing you were acting on or is fixed for this step, so you can make the call from this object alone. Present only when kind is operation and the operation names a subject. A request body, when the operation takes one, is described by the operation's own schema and never appears here.
data.next.url
string
A URL to open. Present only when kind is external, and only when the step has one. An external step whose description says to go and do something with no URL to open is normal.
next_cursor
nullable string
required
Cursor for the next page. Pass back as starting_after to advance forward. null when no next page exists.
prev_cursor
nullable string
required
Cursor for the previous page. Pass back as ending_before to step backward. null when no previous page exists.
refresh_cursor
nullable string
required
Refresh anchor, the first row of this response. Pass back as ending_before to fetch what precedes it in the current sort order. On a newest-first sort those are the items that have appeared since; on any other sort they are the items that sort earlier, so refreshing such a list means re-fetching it instead. Non-null whenever data is non-empty; null only on an empty page. Distinct from prev_cursor.