Templates API
Template groups, AI localization and Meta approval status. 30 ops: list_template_languages, list_templates, get_template, create_template, update_template, save_template_localization, translate_template, review_template_localization, validate_template, check_template_content, submit_template, submit
Template groups, AI localization and Meta approval status.
List template languages · POST /api/v1/list_template_languages · scope templates:read or GET …?input=
Every language code Meta accepts for WhatsApp templates (en_US, es, pt_BR, ar…), with its name and whether it is written right to left. Use these codes as locales everywhere in the template ops.
curl -X POST https://getwabi.dev/api/v1/list_template_languages \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi list_template_languages{
"type": "object",
"properties": {}
}list_templates
readList templates · POST /api/v1/list_templates · scope templates:read or GET …?input=
The organization's message templates: one entry per logical template (name and category) with every locale's newest version, its status (draft, submitted, pending, approved, rejected, paused, disabled), review state, quality rating, rejection reason and the version sends use. Filter by status, category, account or a search over name and description.
curl -X POST https://getwabi.dev/api/v1/list_templates \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi list_templates{
"type": "object",
"properties": {
"status": {
"description": "Only templates with at least one locale in this status.",
"type": "string",
"enum": [
"draft",
"submitted",
"pending",
"approved",
"rejected",
"paused",
"disabled"
]
},
"category": {
"type": "string",
"enum": [
"utility",
"marketing",
"authentication"
],
"description": "utility (transactional updates the customer expects), marketing (promotions, re-engagement, surveys) or authentication (one-time passwords)."
},
"account_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "WhatsApp Business Account id in this organization (whatsapp_accounts.id, not Meta's WABA id)."
},
"search": {
"type": "string",
"maxLength": 200
},
"include_archived": {
"default": false,
"type": "boolean"
},
"limit": {
"default": 50,
"description": "Page size, at most 200.",
"type": "integer",
"minimum": 1,
"maximum": 200
},
"offset": {
"default": 0,
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
}
}get_template
readGet a template · POST /api/v1/get_template · scope templates:read or GET …?input=
One template with everything about it: variables with examples, every locale and version (content, Meta components, validation issues, AI translation notes, review), Meta submissions, status and quality history, and open alerts. Call it before editing, reviewing or submitting.
curl -X POST https://getwabi.dev/api/v1/get_template \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"template_id":"template_id"}'npx @meterapp/wabi get_template --json '{"template_id":"template_id"}'{
"type": "object",
"properties": {
"template_id": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"description": "Template id (template_groups.id from list_templates), or its name when only one WhatsApp account has a template of that name."
}
},
"required": [
"template_id"
]
}create_template
writeCreate a template · POST /api/v1/create_template · scope templates:write
Creates a logical template (one name and category, with a localization per language) and its default-locale draft. Content uses {{variable}} placeholders with sample values in variables or content.examples. Nothing reaches Meta until submit_template. Defaults to the only connected account, else the sandbox. Returns the template with its validation result, so problems show before submission. Then translate_template for more languages.
curl -X POST https://getwabi.dev/api/v1/create_template \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"name":"Support","category":"utility","content":{}}'npx @meterapp/wabi create_template --json '{"name":"Support","category":"utility","content":{}}'{
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"description": "Meta template name: lowercase letters, digits and underscores, e.g. booking_confirmation."
},
"category": {
"type": "string",
"enum": [
"utility",
"marketing",
"authentication"
],
"description": "utility (transactional updates the customer expects), marketing (promotions, re-engagement, surveys) or authentication (one-time passwords)."
},
"default_locale": {
"default": "en",
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
},
"content": {
"type": "object",
"properties": {
"header": {
"oneOf": [
{
"type": "object",
"properties": {
"format": {
"type": "string",
"const": "text"
},
"text": {
"type": "string",
"maxLength": 240,
"description": "Header text, at most 60 characters and one variable."
}
},
"required": [
"format",
"text"
]
},
{
"type": "object",
"properties": {
"format": {
"type": "string",
"enum": [
"image",
"video",
"document"
]
},
"handle": {
"description": "A media upload handle from Meta's resumable upload API, the example Meta's review looks at. Sandbox templates accept any value.",
"type": "string",
"maxLength": 2000
},
"example_url": {
"description": "A public https link to a sample image, video or PDF, for previews and the sandbox.",
"type": "string",
"maxLength": 2000,
"format": "uri"
}
},
"required": [
"format"
]
},
{
"type": "object",
"properties": {
"format": {
"type": "string",
"const": "location"
}
},
"required": [
"format"
]
}
],
"description": "Optional header: text, a media type (image, video, document) or a location (utility and marketing only)."
},
"body": {
"default": "",
"description": "Body text, at most 1,024 characters, with {{variables}}. WhatsApp formatting: *bold*, _italic_, ~strike~, ```mono```. Ignored for authentication.",
"type": "string",
"maxLength": 4096
},
"footer": {
"description": "Footer text, at most 60 characters, no variables.",
"type": "string",
"maxLength": 240
},
"buttons": {
"maxItems": 20,
"type": "array",
"items": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "quick_reply"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label, at most 25 characters."
}
},
"required": [
"type",
"text"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "url"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label, at most 25 characters."
},
"url": {
"type": "string",
"maxLength": 2000,
"description": "https URL; may end with {{1}} for a per-message suffix."
},
"example": {
"description": "The full URL with a sample suffix, required when url ends with {{1}}.",
"type": "string",
"maxLength": 2000
}
},
"required": [
"type",
"text",
"url"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "phone_number"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label."
},
"phone_number": {
"type": "string",
"maxLength": 20,
"description": "E.164 number, e.g. +15550100001."
}
},
"required": [
"type",
"text",
"phone_number"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "copy_code"
},
"example": {
"type": "string",
"maxLength": 60,
"description": "Sample coupon code, at most 15 characters (marketing)."
}
},
"required": [
"type",
"example"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "otp"
},
"otp_type": {
"default": "copy_code",
"type": "string",
"enum": [
"copy_code",
"one_tap",
"zero_tap"
]
},
"text": {
"description": "Copy-code button label. Default: Meta's localized 'Copy code'.",
"type": "string",
"maxLength": 100
},
"autofill_text": {
"type": "string",
"maxLength": 100
},
"package_name": {
"description": "Android package name, for one_tap and zero_tap.",
"type": "string",
"maxLength": 224
},
"signature_hash": {
"description": "Android app signing key hash, for one_tap and zero_tap.",
"type": "string",
"maxLength": 11
}
},
"required": [
"type"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "flow"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label."
},
"flow_id": {
"type": "string",
"maxLength": 64
},
"flow_action": {
"default": "navigate",
"type": "string",
"enum": [
"navigate",
"data_exchange"
]
},
"navigate_screen": {
"type": "string",
"maxLength": 100
}
},
"required": [
"type",
"text",
"flow_id"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "catalog"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label, e.g. View catalog."
}
},
"required": [
"type",
"text"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "mpm"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label, e.g. View items."
}
},
"required": [
"type",
"text"
]
}
],
"description": "A button: quick_reply, url, phone_number, copy_code (marketing), otp (authentication), flow, catalog or mpm (marketing)."
}
},
"examples": {
"description": "Sample value per variable for this locale ({ guest_name: 'Ana' } or { '1': 'Ana' }); default: the template's variable examples.",
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string",
"maxLength": 1000
}
},
"add_security_recommendation": {
"description": "Authentication: add Meta's 'For your security, do not share this code.'",
"type": "boolean"
},
"code_expiration_minutes": {
"description": "Authentication: show 'This code expires in N minutes.' (1–90).",
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
}
}
},
"variables": {
"description": "Each variable's meaning and sample value. Variables used in the content but not listed are added with the content's examples.",
"maxItems": 50,
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"description": "The variable as written between braces: guest_name (named) or 1 (positional)."
},
"description": {
"default": "",
"description": "What it holds, for translators and senders.",
"type": "string",
"maxLength": 500
},
"example": {
"default": "",
"description": "A realistic sample value; Meta rejects templates whose variables have none.",
"type": "string",
"maxLength": 1000
}
},
"required": [
"name"
]
}
},
"parameter_format": {
"description": "named ({{guest_name}}, the default) or positional ({{1}}). Inferred from the content when omitted.",
"type": "string",
"enum": [
"named",
"positional"
]
},
"description": {
"description": "What the template is for; translators and reviewers read it.",
"type": "string",
"maxLength": 2000
},
"target_locales": {
"description": "Languages it should exist in; missing ones are reported until drafted.",
"maxItems": 100,
"type": "array",
"items": {
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
}
},
"account_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "WhatsApp Business Account id in this organization (whatsapp_accounts.id, not Meta's WABA id)."
}
},
"required": [
"name",
"category",
"content"
]
}update_template
writeUpdate a template · POST /api/v1/update_template · scope templates:write
Changes template-level settings: description, variables (meaning and samples), target locales, locale fallbacks, the default locale, or the category while nothing has been submitted. Locale content is edited with save_template_localization.
curl -X POST https://getwabi.dev/api/v1/update_template \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"template_id":"template_id"}'npx @meterapp/wabi update_template --json '{"template_id":"template_id"}'{
"type": "object",
"properties": {
"template_id": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"description": "Template id (template_groups.id from list_templates), or its name when only one WhatsApp account has a template of that name."
},
"description": {
"type": "string",
"maxLength": 2000
},
"variables": {
"maxItems": 50,
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"description": "The variable as written between braces: guest_name (named) or 1 (positional)."
},
"description": {
"default": "",
"description": "What it holds, for translators and senders.",
"type": "string",
"maxLength": 500
},
"example": {
"default": "",
"description": "A realistic sample value; Meta rejects templates whose variables have none.",
"type": "string",
"maxLength": 1000
}
},
"required": [
"name"
]
}
},
"target_locales": {
"maxItems": 100,
"type": "array",
"items": {
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
}
},
"locale_fallbacks": {
"type": "object",
"properties": {
"default": {
"description": "Approved locales to send, in order, when the requested one has none. Default: the template's default locale.",
"maxItems": 20,
"type": "array",
"items": {
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
}
},
"by_locale": {
"description": "Per requested locale, e.g. { \"es_MX\": [\"es\", \"es_ES\"] }.",
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"maxItems": 20,
"type": "array",
"items": {
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
}
}
},
"same_language": {
"description": "Allow another variant of the same language (es_MX for es). Default true.",
"type": "boolean"
}
},
"description": "Locale fallbacks. Only approved localizations are ever used, and every substitution is reported."
},
"default_locale": {
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
},
"category": {
"type": "string",
"enum": [
"utility",
"marketing",
"authentication"
],
"description": "utility (transactional updates the customer expects), marketing (promotions, re-engagement, surveys) or authentication (one-time passwords)."
}
},
"required": [
"template_id"
]
}Save a template locale · POST /api/v1/save_template_localization · scope templates:write
Writes one locale's content (header, body, footer, buttons, examples). A draft or rejected version is updated in place; an approved, paused or disabled version gets a new version, submitted later as its own Meta template (<name>_v<n>) while the approved one keeps sending. Refused while Meta is reviewing it. Returns the localization with its validation result.
curl -X POST https://getwabi.dev/api/v1/save_template_localization \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"template_id":"template_id","locale":"es","content":{}}'npx @meterapp/wabi save_template_localization --json '{"template_id":"template_id","locale":"es","content":{}}'{
"type": "object",
"properties": {
"template_id": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"description": "Template id (template_groups.id from list_templates), or its name when only one WhatsApp account has a template of that name."
},
"locale": {
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
},
"content": {
"type": "object",
"properties": {
"header": {
"oneOf": [
{
"type": "object",
"properties": {
"format": {
"type": "string",
"const": "text"
},
"text": {
"type": "string",
"maxLength": 240,
"description": "Header text, at most 60 characters and one variable."
}
},
"required": [
"format",
"text"
]
},
{
"type": "object",
"properties": {
"format": {
"type": "string",
"enum": [
"image",
"video",
"document"
]
},
"handle": {
"description": "A media upload handle from Meta's resumable upload API, the example Meta's review looks at. Sandbox templates accept any value.",
"type": "string",
"maxLength": 2000
},
"example_url": {
"description": "A public https link to a sample image, video or PDF, for previews and the sandbox.",
"type": "string",
"maxLength": 2000,
"format": "uri"
}
},
"required": [
"format"
]
},
{
"type": "object",
"properties": {
"format": {
"type": "string",
"const": "location"
}
},
"required": [
"format"
]
}
],
"description": "Optional header: text, a media type (image, video, document) or a location (utility and marketing only)."
},
"body": {
"default": "",
"description": "Body text, at most 1,024 characters, with {{variables}}. WhatsApp formatting: *bold*, _italic_, ~strike~, ```mono```. Ignored for authentication.",
"type": "string",
"maxLength": 4096
},
"footer": {
"description": "Footer text, at most 60 characters, no variables.",
"type": "string",
"maxLength": 240
},
"buttons": {
"maxItems": 20,
"type": "array",
"items": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "quick_reply"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label, at most 25 characters."
}
},
"required": [
"type",
"text"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "url"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label, at most 25 characters."
},
"url": {
"type": "string",
"maxLength": 2000,
"description": "https URL; may end with {{1}} for a per-message suffix."
},
"example": {
"description": "The full URL with a sample suffix, required when url ends with {{1}}.",
"type": "string",
"maxLength": 2000
}
},
"required": [
"type",
"text",
"url"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "phone_number"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label."
},
"phone_number": {
"type": "string",
"maxLength": 20,
"description": "E.164 number, e.g. +15550100001."
}
},
"required": [
"type",
"text",
"phone_number"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "copy_code"
},
"example": {
"type": "string",
"maxLength": 60,
"description": "Sample coupon code, at most 15 characters (marketing)."
}
},
"required": [
"type",
"example"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "otp"
},
"otp_type": {
"default": "copy_code",
"type": "string",
"enum": [
"copy_code",
"one_tap",
"zero_tap"
]
},
"text": {
"description": "Copy-code button label. Default: Meta's localized 'Copy code'.",
"type": "string",
"maxLength": 100
},
"autofill_text": {
"type": "string",
"maxLength": 100
},
"package_name": {
"description": "Android package name, for one_tap and zero_tap.",
"type": "string",
"maxLength": 224
},
"signature_hash": {
"description": "Android app signing key hash, for one_tap and zero_tap.",
"type": "string",
"maxLength": 11
}
},
"required": [
"type"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "flow"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label."
},
"flow_id": {
"type": "string",
"maxLength": 64
},
"flow_action": {
"default": "navigate",
"type": "string",
"enum": [
"navigate",
"data_exchange"
]
},
"navigate_screen": {
"type": "string",
"maxLength": 100
}
},
"required": [
"type",
"text",
"flow_id"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "catalog"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label, e.g. View catalog."
}
},
"required": [
"type",
"text"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "mpm"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label, e.g. View items."
}
},
"required": [
"type",
"text"
]
}
],
"description": "A button: quick_reply, url, phone_number, copy_code (marketing), otp (authentication), flow, catalog or mpm (marketing)."
}
},
"examples": {
"description": "Sample value per variable for this locale ({ guest_name: 'Ana' } or { '1': 'Ana' }); default: the template's variable examples.",
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string",
"maxLength": 1000
}
},
"add_security_recommendation": {
"description": "Authentication: add Meta's 'For your security, do not share this code.'",
"type": "boolean"
},
"code_expiration_minutes": {
"description": "Authentication: show 'This code expires in N minutes.' (1–90).",
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
}
}
}
},
"required": [
"template_id",
"locale",
"content"
]
}translate_template
writeTranslate a template with AI · POST /api/v1/translate_template · scope templates:write
Drafts more locales of a template with AI (Claude): the brand glossary applied, variables protected, sample values localized, parameters checked against the source. Each draft gets a confidence and a reviewer note and must be approved with review_template_localization before submission. Never overwrites a locale a person wrote, reviewed or submitted (overwrite: true redoes unreviewed AI drafts only).
curl -X POST https://getwabi.dev/api/v1/translate_template \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"template_id":"template_id","locales":["es"]}'npx @meterapp/wabi translate_template --json '{"template_id":"template_id","locales":["es"]}'{
"type": "object",
"properties": {
"template_id": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"description": "Template id (template_groups.id from list_templates), or its name when only one WhatsApp account has a template of that name."
},
"locales": {
"minItems": 1,
"maxItems": 30,
"type": "array",
"items": {
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
},
"description": "Target locales, e.g. [\"es\", \"fr\"]."
},
"source_locale": {
"description": "Translate from this locale. Default: the template's default locale.",
"type": "string",
"minLength": 2,
"maxLength": 35
},
"overwrite": {
"default": false,
"type": "boolean"
}
},
"required": [
"template_id",
"locales"
]
}Review a template locale · POST /api/v1/review_template_localization · scope templates:write
Approves a locale's draft for submission, or requests changes with a note. AI drafts can only be submitted once approved here. Applies to the newest version unless version is given.
curl -X POST https://getwabi.dev/api/v1/review_template_localization \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"template_id":"template_id","locale":"es","decision":"approve"}'npx @meterapp/wabi review_template_localization --json '{"template_id":"template_id","locale":"es","decision":"approve"}'{
"type": "object",
"properties": {
"template_id": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"description": "Template id (template_groups.id from list_templates), or its name when only one WhatsApp account has a template of that name."
},
"locale": {
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
},
"decision": {
"type": "string",
"enum": [
"approve",
"request_changes"
]
},
"note": {
"type": "string",
"maxLength": 2000
},
"version": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"required": [
"template_id",
"locale",
"decision"
]
}Validate a template · POST /api/v1/validate_template · scope templates:read or GET …?input=
Checks every locale (or the ones given) against Meta's rules before submission: name, language code, header/body/footer limits, variables and their samples, buttons, authentication format, likely category mismatch, parameter consistency across locales, glossary use and missing target locales. Errors block submit_template; warnings are common rejection reasons. Changes nothing.
curl -X POST https://getwabi.dev/api/v1/validate_template \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"template_id":"template_id"}'npx @meterapp/wabi validate_template --json '{"template_id":"template_id"}'{
"type": "object",
"properties": {
"template_id": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"description": "Template id (template_groups.id from list_templates), or its name when only one WhatsApp account has a template of that name."
},
"locales": {
"maxItems": 100,
"type": "array",
"items": {
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
}
}
},
"required": [
"template_id"
]
}Check template content · POST /api/v1/check_template_content · scope templates:read or GET …?input=
Validates content that is not saved yet (a builder preview, a file before apply_templates) against Meta's rules and the category, without touching any template. Returns the same issues as validate_template.
curl -X POST https://getwabi.dev/api/v1/check_template_content \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"name":"Support","category":"utility","content":{}}'npx @meterapp/wabi check_template_content --json '{"name":"Support","category":"utility","content":{}}'{
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 512
},
"category": {
"type": "string",
"enum": [
"utility",
"marketing",
"authentication"
],
"description": "utility (transactional updates the customer expects), marketing (promotions, re-engagement, surveys) or authentication (one-time passwords)."
},
"locale": {
"default": "en",
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
},
"content": {
"type": "object",
"properties": {
"header": {
"oneOf": [
{
"type": "object",
"properties": {
"format": {
"type": "string",
"const": "text"
},
"text": {
"type": "string",
"maxLength": 240,
"description": "Header text, at most 60 characters and one variable."
}
},
"required": [
"format",
"text"
]
},
{
"type": "object",
"properties": {
"format": {
"type": "string",
"enum": [
"image",
"video",
"document"
]
},
"handle": {
"description": "A media upload handle from Meta's resumable upload API, the example Meta's review looks at. Sandbox templates accept any value.",
"type": "string",
"maxLength": 2000
},
"example_url": {
"description": "A public https link to a sample image, video or PDF, for previews and the sandbox.",
"type": "string",
"maxLength": 2000,
"format": "uri"
}
},
"required": [
"format"
]
},
{
"type": "object",
"properties": {
"format": {
"type": "string",
"const": "location"
}
},
"required": [
"format"
]
}
],
"description": "Optional header: text, a media type (image, video, document) or a location (utility and marketing only)."
},
"body": {
"default": "",
"description": "Body text, at most 1,024 characters, with {{variables}}. WhatsApp formatting: *bold*, _italic_, ~strike~, ```mono```. Ignored for authentication.",
"type": "string",
"maxLength": 4096
},
"footer": {
"description": "Footer text, at most 60 characters, no variables.",
"type": "string",
"maxLength": 240
},
"buttons": {
"maxItems": 20,
"type": "array",
"items": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "quick_reply"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label, at most 25 characters."
}
},
"required": [
"type",
"text"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "url"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label, at most 25 characters."
},
"url": {
"type": "string",
"maxLength": 2000,
"description": "https URL; may end with {{1}} for a per-message suffix."
},
"example": {
"description": "The full URL with a sample suffix, required when url ends with {{1}}.",
"type": "string",
"maxLength": 2000
}
},
"required": [
"type",
"text",
"url"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "phone_number"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label."
},
"phone_number": {
"type": "string",
"maxLength": 20,
"description": "E.164 number, e.g. +15550100001."
}
},
"required": [
"type",
"text",
"phone_number"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "copy_code"
},
"example": {
"type": "string",
"maxLength": 60,
"description": "Sample coupon code, at most 15 characters (marketing)."
}
},
"required": [
"type",
"example"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "otp"
},
"otp_type": {
"default": "copy_code",
"type": "string",
"enum": [
"copy_code",
"one_tap",
"zero_tap"
]
},
"text": {
"description": "Copy-code button label. Default: Meta's localized 'Copy code'.",
"type": "string",
"maxLength": 100
},
"autofill_text": {
"type": "string",
"maxLength": 100
},
"package_name": {
"description": "Android package name, for one_tap and zero_tap.",
"type": "string",
"maxLength": 224
},
"signature_hash": {
"description": "Android app signing key hash, for one_tap and zero_tap.",
"type": "string",
"maxLength": 11
}
},
"required": [
"type"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "flow"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label."
},
"flow_id": {
"type": "string",
"maxLength": 64
},
"flow_action": {
"default": "navigate",
"type": "string",
"enum": [
"navigate",
"data_exchange"
]
},
"navigate_screen": {
"type": "string",
"maxLength": 100
}
},
"required": [
"type",
"text",
"flow_id"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "catalog"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label, e.g. View catalog."
}
},
"required": [
"type",
"text"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "mpm"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label, e.g. View items."
}
},
"required": [
"type",
"text"
]
}
],
"description": "A button: quick_reply, url, phone_number, copy_code (marketing), otp (authentication), flow, catalog or mpm (marketing)."
}
},
"examples": {
"description": "Sample value per variable for this locale ({ guest_name: 'Ana' } or { '1': 'Ana' }); default: the template's variable examples.",
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string",
"maxLength": 1000
}
},
"add_security_recommendation": {
"description": "Authentication: add Meta's 'For your security, do not share this code.'",
"type": "boolean"
},
"code_expiration_minutes": {
"description": "Authentication: show 'This code expires in N minutes.' (1–90).",
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
}
}
},
"parameter_format": {
"default": "named",
"type": "string",
"enum": [
"named",
"positional"
]
},
"variables": {
"maxItems": 50,
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"description": "The variable as written between braces: guest_name (named) or 1 (positional)."
},
"description": {
"default": "",
"description": "What it holds, for translators and senders.",
"type": "string",
"maxLength": 500
},
"example": {
"default": "",
"description": "A realistic sample value; Meta rejects templates whose variables have none.",
"type": "string",
"maxLength": 1000
}
},
"required": [
"name"
]
}
}
},
"required": [
"name",
"category",
"content"
]
}submit_template
writeSubmit a template to Meta · POST /api/v1/submit_template · scope templates:write
Submits a template's ready locales (or the ones given) to Meta for review, each as its own Meta template. Each locale must pass validation and, for AI drafts, review; a likely category mismatch blocks unless accept_category_warnings is true. Returns a result per locale (submitted, invalid, blocked, needs_review, failed, skipped). Meta decides within minutes to a day (the sandbox in ~60 s); statuses then update from webhooks and sync_templates.
curl -X POST https://getwabi.dev/api/v1/submit_template \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"template_id":"template_id"}'npx @meterapp/wabi submit_template --json '{"template_id":"template_id"}'{
"type": "object",
"properties": {
"template_id": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"description": "Template id (template_groups.id from list_templates), or its name when only one WhatsApp account has a template of that name."
},
"locales": {
"maxItems": 100,
"type": "array",
"items": {
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
}
},
"accept_category_warnings": {
"default": false,
"type": "boolean"
}
},
"required": [
"template_id"
]
}submit_templates
writeSubmit templates in bulk · POST /api/v1/submit_templates · scope templates:write
Bulk submission: every ready draft locale of the given templates, or of every template when template_ids is omitted. Same checks and per-locale results as submit_template, grouped by template.
curl -X POST https://getwabi.dev/api/v1/submit_templates \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi submit_templates{
"type": "object",
"properties": {
"template_ids": {
"maxItems": 200,
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"description": "Template id (template_groups.id from list_templates), or its name when only one WhatsApp account has a template of that name."
}
},
"accept_category_warnings": {
"default": false,
"type": "boolean"
}
}
}sync_templates
writeSync template statuses · POST /api/v1/sync_templates · scope templates:write
Reconciles every submitted template's status, quality rating, rejection reason and category with Meta now (it also runs on its own every few minutes, and webhooks update statuses as they happen). Returns what changed and templates Meta no longer has.
curl -X POST https://getwabi.dev/api/v1/sync_templates \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi sync_templates{
"type": "object",
"properties": {
"account_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "WhatsApp Business Account id in this organization (whatsapp_accounts.id, not Meta's WABA id)."
}
}
}import_templates
writeImport templates from a WABA · POST /api/v1/import_templates · scope templates:write
Brings the templates that already exist on a connected WhatsApp Business Account under management (grouped by name, one localization per language, with Meta's status and quality), so they can be translated, analyzed and replaced here. Skips ones already tracked. Defaults to the only connected account, else the sandbox.
curl -X POST https://getwabi.dev/api/v1/import_templates \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi import_templates{
"type": "object",
"properties": {
"account_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "WhatsApp Business Account id in this organization (whatsapp_accounts.id, not Meta's WABA id)."
},
"names": {
"description": "Only these Meta template names.",
"maxItems": 200,
"type": "array",
"items": {
"type": "string",
"maxLength": 512
}
}
}
}resolve_template
readResolve the template to send · POST /api/v1/resolve_template · scope templates:read or GET …?input=
Which approved localization to send for a locale or a contact (their saved locale): the exact locale, then configured fallbacks, then another variant of the language, then the default locale. Never picks an unapproved one; reports any substitution in fallback and why each other locale was skipped. Returns Meta's name and language for send_message and the parameters to pass. 409 no_approved_locale when nothing approved fits.
curl -X POST https://getwabi.dev/api/v1/resolve_template \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"template_id":"template_id"}'npx @meterapp/wabi resolve_template --json '{"template_id":"template_id"}'{
"type": "object",
"properties": {
"template_id": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"description": "Template id (template_groups.id from list_templates), or its name when only one WhatsApp account has a template of that name."
},
"locale": {
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
},
"contact_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Contact id."
}
},
"required": [
"template_id"
]
}pin_template_version
writePin a template version (rollback) · POST /api/v1/pin_template_version · scope templates:write
Chooses which approved version of a locale sends use: a rollback to an earlier approved version, or back to the newest approved one with version null. Only approved versions can be pinned.
curl -X POST https://getwabi.dev/api/v1/pin_template_version \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"template_id":"template_id","locale":"es","version":1}'npx @meterapp/wabi pin_template_version --json '{"template_id":"template_id","locale":"es","version":1}'{
"type": "object",
"properties": {
"template_id": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"description": "Template id (template_groups.id from list_templates), or its name when only one WhatsApp account has a template of that name."
},
"locale": {
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
},
"version": {
"anyOf": [
{
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
{
"type": "null"
}
]
}
},
"required": [
"template_id",
"locale",
"version"
]
}Compare template versions · POST /api/v1/compare_template_versions · scope templates:read or GET …?input=
Field-by-field differences between two versions of one locale (header, body, footer, buttons, examples), with each version's status and Meta name.
curl -X POST https://getwabi.dev/api/v1/compare_template_versions \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"template_id":"template_id","locale":"es","from":1,"to":1}'npx @meterapp/wabi compare_template_versions --json '{"template_id":"template_id","locale":"es","from":1,"to":1}'{
"type": "object",
"properties": {
"template_id": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"description": "Template id (template_groups.id from list_templates), or its name when only one WhatsApp account has a template of that name."
},
"locale": {
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
},
"from": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"to": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"required": [
"template_id",
"locale",
"from",
"to"
]
}archive_template
writeArchive a template · POST /api/v1/archive_template · scope templates:write
Hides a template from lists and stops it from being edited or submitted. Its Meta templates are left as they are (delete them in Meta if they should stop existing) and its history stays.
curl -X POST https://getwabi.dev/api/v1/archive_template \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"template_id":"template_id"}'npx @meterapp/wabi archive_template --json '{"template_id":"template_id"}'{
"type": "object",
"properties": {
"template_id": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"description": "Template id (template_groups.id from list_templates), or its name when only one WhatsApp account has a template of that name."
}
},
"required": [
"template_id"
]
}Delete a template draft · POST /api/v1/delete_template_draft · scope templates:write
Deletes a locale's draft version that was never submitted to Meta (the newest unless version is given). Submitted versions are kept for their history.
curl -X POST https://getwabi.dev/api/v1/delete_template_draft \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"template_id":"template_id","locale":"es"}'npx @meterapp/wabi delete_template_draft --json '{"template_id":"template_id","locale":"es"}'{
"type": "object",
"properties": {
"template_id": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"description": "Template id (template_groups.id from list_templates), or its name when only one WhatsApp account has a template of that name."
},
"locale": {
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
},
"version": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"required": [
"template_id",
"locale"
]
}List starter templates · POST /api/v1/list_template_library · scope templates:read or GET …?input=
The starter library: ready-to-submit templates for one-time passwords, booking confirmations, appointment reminders, driver assignment, payment receipts, delivery and trip updates, support cases, cancellations and schedule changes, feedback and re-engagement, each in the category Meta would give it and why.
curl -X POST https://getwabi.dev/api/v1/list_template_library \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi list_template_library{
"type": "object",
"properties": {}
}Install starter templates · POST /api/v1/install_template_library · scope templates:write
Adds starter templates (all, or the keys given) to an account as English drafts, optionally drafting more locales with AI and submitting the English ones. Skips any already installed. Returns each template's id.
curl -X POST https://getwabi.dev/api/v1/install_template_library \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi install_template_library{
"type": "object",
"properties": {
"keys": {
"description": "Library keys from list_template_library. Default: all.",
"maxItems": 50,
"type": "array",
"items": {
"type": "string",
"maxLength": 64
}
},
"locales": {
"description": "Also draft these locales with AI (reviewed before submission).",
"maxItems": 30,
"type": "array",
"items": {
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
}
},
"submit": {
"default": false,
"description": "Submit the English versions right away.",
"type": "boolean"
},
"account_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "WhatsApp Business Account id in this organization (whatsapp_accounts.id, not Meta's WABA id)."
}
}
}apply_templates
writeApply template definitions · POST /api/v1/apply_templates · scope templates:write
Syncs templates from a definition file kept in a repository (YAML or JSON text in source, or the parsed object in definitions): creates missing templates and locales, updates changed drafts, opens replacement versions for changed approved locales, drafts translate: locales with AI and submits when submit: true. dry_run: true returns the plan without changing anything. Idempotent: applying the same file twice changes nothing. Never deletes. CLI: wabi apply_templates --source "$(cat templates.yaml)" --dry_run true.
curl -X POST https://getwabi.dev/api/v1/apply_templates \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi apply_templates{
"type": "object",
"properties": {
"source": {
"description": "The file's text, YAML or JSON (version: 1, templates: [...]).",
"type": "string",
"maxLength": 500000
},
"definitions": {
"type": "object",
"properties": {
"version": {
"default": 1,
"type": "number",
"const": 1
},
"account_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"templates": {
"minItems": 1,
"maxItems": 200,
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 512
},
"category": {
"type": "string",
"enum": [
"utility",
"marketing",
"authentication"
]
},
"description": {
"type": "string",
"maxLength": 2000
},
"default_locale": {
"default": "en",
"type": "string",
"minLength": 2,
"maxLength": 35
},
"parameter_format": {
"type": "string",
"enum": [
"named",
"positional"
]
},
"variables": {
"anyOf": [
{
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"description": "The variable as written between braces: guest_name (named) or 1 (positional)."
},
"description": {
"default": "",
"description": "What it holds, for translators and senders.",
"type": "string",
"maxLength": 500
},
"example": {
"default": "",
"description": "A realistic sample value; Meta rejects templates whose variables have none.",
"type": "string",
"maxLength": 1000
}
},
"required": [
"name"
]
}
},
{
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "object",
"properties": {
"description": {
"default": "",
"type": "string",
"maxLength": 500
},
"example": {
"default": "",
"type": "string",
"maxLength": 1000
}
}
}
}
]
},
"target_locales": {
"maxItems": 100,
"type": "array",
"items": {
"type": "string",
"minLength": 2,
"maxLength": 35
}
},
"locale_fallbacks": {
"type": "object",
"properties": {
"default": {
"type": "array",
"items": {
"type": "string"
}
},
"by_locale": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
},
"same_language": {
"type": "boolean"
}
}
},
"locales": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "object",
"properties": {
"header": {
"oneOf": [
{
"type": "object",
"properties": {
"format": {
"type": "string",
"const": "text"
},
"text": {
"type": "string",
"maxLength": 240,
"description": "Header text, at most 60 characters and one variable."
}
},
"required": [
"format",
"text"
]
},
{
"type": "object",
"properties": {
"format": {
"type": "string",
"enum": [
"image",
"video",
"document"
]
},
"handle": {
"description": "A media upload handle from Meta's resumable upload API, the example Meta's review looks at. Sandbox templates accept any value.",
"type": "string",
"maxLength": 2000
},
"example_url": {
"description": "A public https link to a sample image, video or PDF, for previews and the sandbox.",
"type": "string",
"maxLength": 2000,
"format": "uri"
}
},
"required": [
"format"
]
},
{
"type": "object",
"properties": {
"format": {
"type": "string",
"const": "location"
}
},
"required": [
"format"
]
}
],
"description": "Optional header: text, a media type (image, video, document) or a location (utility and marketing only)."
},
"body": {
"default": "",
"description": "Body text, at most 1,024 characters, with {{variables}}. WhatsApp formatting: *bold*, _italic_, ~strike~, ```mono```. Ignored for authentication.",
"type": "string",
"maxLength": 4096
},
"footer": {
"description": "Footer text, at most 60 characters, no variables.",
"type": "string",
"maxLength": 240
},
"buttons": {
"maxItems": 20,
"type": "array",
"items": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "quick_reply"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label, at most 25 characters."
}
},
"required": [
"type",
"text"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "url"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label, at most 25 characters."
},
"url": {
"type": "string",
"maxLength": 2000,
"description": "https URL; may end with {{1}} for a per-message suffix."
},
"example": {
"description": "The full URL with a sample suffix, required when url ends with {{1}}.",
"type": "string",
"maxLength": 2000
}
},
"required": [
"type",
"text",
"url"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "phone_number"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label."
},
"phone_number": {
"type": "string",
"maxLength": 20,
"description": "E.164 number, e.g. +15550100001."
}
},
"required": [
"type",
"text",
"phone_number"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "copy_code"
},
"example": {
"type": "string",
"maxLength": 60,
"description": "Sample coupon code, at most 15 characters (marketing)."
}
},
"required": [
"type",
"example"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "otp"
},
"otp_type": {
"default": "copy_code",
"type": "string",
"enum": [
"copy_code",
"one_tap",
"zero_tap"
]
},
"text": {
"description": "Copy-code button label. Default: Meta's localized 'Copy code'.",
"type": "string",
"maxLength": 100
},
"autofill_text": {
"type": "string",
"maxLength": 100
},
"package_name": {
"description": "Android package name, for one_tap and zero_tap.",
"type": "string",
"maxLength": 224
},
"signature_hash": {
"description": "Android app signing key hash, for one_tap and zero_tap.",
"type": "string",
"maxLength": 11
}
},
"required": [
"type"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "flow"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label."
},
"flow_id": {
"type": "string",
"maxLength": 64
},
"flow_action": {
"default": "navigate",
"type": "string",
"enum": [
"navigate",
"data_exchange"
]
},
"navigate_screen": {
"type": "string",
"maxLength": 100
}
},
"required": [
"type",
"text",
"flow_id"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "catalog"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label, e.g. View catalog."
}
},
"required": [
"type",
"text"
]
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "mpm"
},
"text": {
"type": "string",
"maxLength": 100,
"description": "Button label, e.g. View items."
}
},
"required": [
"type",
"text"
]
}
],
"description": "A button: quick_reply, url, phone_number, copy_code (marketing), otp (authentication), flow, catalog or mpm (marketing)."
}
},
"examples": {
"description": "Sample value per variable for this locale ({ guest_name: 'Ana' } or { '1': 'Ana' }); default: the template's variable examples.",
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string",
"maxLength": 1000
}
},
"add_security_recommendation": {
"description": "Authentication: add Meta's 'For your security, do not share this code.'",
"type": "boolean"
},
"code_expiration_minutes": {
"description": "Authentication: show 'This code expires in N minutes.' (1–90).",
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
}
}
}
},
"translate": {
"maxItems": 100,
"type": "array",
"items": {
"type": "string",
"minLength": 2,
"maxLength": 35
}
},
"submit": {
"type": "boolean"
}
},
"required": [
"name",
"category",
"locales"
]
}
}
},
"required": [
"templates"
]
},
"dry_run": {
"default": false,
"type": "boolean"
},
"account_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "WhatsApp Business Account id in this organization (whatsapp_accounts.id, not Meta's WABA id)."
},
"path": {
"description": "Where the file lives (repository and path), recorded on each template it manages.",
"type": "string",
"maxLength": 500
}
}
}export_templates
readExport template definitions · POST /api/v1/export_templates · scope templates:read or GET …?input=
The definition file (YAML or JSON) for existing templates, newest version of each locale, to commit to a repository and manage with apply_templates from then on.
curl -X POST https://getwabi.dev/api/v1/export_templates \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi export_templates{
"type": "object",
"properties": {
"template_ids": {
"maxItems": 200,
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 512,
"description": "Template id (template_groups.id from list_templates), or its name when only one WhatsApp account has a template of that name."
}
},
"format": {
"default": "yaml",
"type": "string",
"enum": [
"yaml",
"json"
]
}
}
}List the brand glossary · POST /api/v1/list_template_glossary · scope templates:read or GET …?input=
The brand glossary used by translate_template: terms kept verbatim in every language (brand and product names) and terms with a mandatory translation per locale.
curl -X POST https://getwabi.dev/api/v1/list_template_glossary \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi list_template_glossary{
"type": "object",
"properties": {}
}Set a glossary term · POST /api/v1/set_template_glossary_term · scope templates:write
Adds or replaces a brand glossary term: do_not_translate for names that stay verbatim, or translations per locale ({ "es": "reserva" }). Applies to translations drafted from now on and to the glossary checks.
curl -X POST https://getwabi.dev/api/v1/set_template_glossary_term \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"term":"term"}'npx @meterapp/wabi set_template_glossary_term --json '{"term":"term"}'{
"type": "object",
"properties": {
"term": {
"type": "string",
"minLength": 1,
"maxLength": 200
},
"do_not_translate": {
"default": false,
"type": "boolean"
},
"translations": {
"default": {},
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string",
"minLength": 1,
"maxLength": 200
}
},
"note": {
"default": "",
"type": "string",
"maxLength": 500
}
},
"required": [
"term"
]
}Delete a glossary term · POST /api/v1/delete_template_glossary_term · scope templates:write
Removes a term from the brand glossary, by id or by the term itself.
curl -X POST https://getwabi.dev/api/v1/delete_template_glossary_term \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"term":"term"}'npx @meterapp/wabi delete_template_glossary_term --json '{"term":"term"}'{
"type": "object",
"properties": {
"term": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "The term, or its id."
}
},
"required": [
"term"
]
}List template alerts · POST /api/v1/list_template_alerts · scope templates:read or GET …?input=
Open alerts about templates: rejected, paused or disabled by Meta, quality dropping to yellow or red, or Meta filing a template under another category. Each says what happened and what to do.
curl -X POST https://getwabi.dev/api/v1/list_template_alerts \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi list_template_alerts{
"type": "object",
"properties": {
"template_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"include_acknowledged": {
"default": false,
"type": "boolean"
},
"limit": {
"default": 50,
"type": "integer",
"minimum": 1,
"maximum": 200
}
}
}Acknowledge a template alert · POST /api/v1/acknowledge_template_alert · scope templates:write
Marks a template alert as handled; it no longer shows as open. A repeat of the same problem opens a new one.
curl -X POST https://getwabi.dev/api/v1/acknowledge_template_alert \ -H "Authorization: Bearer wabi_…" \ -H "Content-Type: application/json" \ -d '{"alert_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'npx @meterapp/wabi acknowledge_template_alert --json '{"alert_id":"6f1c2e0a-4b7d-4c2e-9a51-0d3b8e7f9a10"}'{
"type": "object",
"properties": {
"alert_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
}
},
"required": [
"alert_id"
]
}Template analytics · POST /api/v1/get_template_analytics · scope templates:read or GET …?input=
Per template and locale over a date range (default the last 30 days): attempts, sent, delivered, read, failed, button clicks, replies, opt-outs and their rates from first-party message events, next to Meta's aggregate template analytics and cost, with discrepancies between the two labeled and explained. With template_id, also its status and quality history.
curl -X POST https://getwabi.dev/api/v1/get_template_analytics \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi get_template_analytics{
"type": "object",
"properties": {
"from": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "UTC day, YYYY-MM-DD."
},
"to": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "UTC day, YYYY-MM-DD."
},
"template_id": {
"type": "string",
"maxLength": 512
},
"locale": {
"type": "string",
"minLength": 2,
"maxLength": 35,
"description": "BCP 47 or Meta template language code, e.g. en_US, es, pt_BR."
},
"category": {
"type": "string",
"enum": [
"utility",
"marketing",
"authentication"
],
"description": "utility (transactional updates the customer expects), marketing (promotions, re-engagement, surveys) or authentication (one-time passwords)."
},
"account_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "WhatsApp Business Account id in this organization (whatsapp_accounts.id, not Meta's WABA id)."
}
}
}Refresh template analytics · POST /api/v1/refresh_template_analytics · scope templates:write
Recomputes first-party template numbers from messages and pulls Meta's template analytics for the date range (default the last 30 days). Runs on its own every hour; call it to see the latest now.
curl -X POST https://getwabi.dev/api/v1/refresh_template_analytics \ -H "Authorization: Bearer wabi_…"npx @meterapp/wabi refresh_template_analytics{
"type": "object",
"properties": {
"from": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "UTC day, YYYY-MM-DD."
},
"to": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "UTC day, YYYY-MM-DD."
},
"account_id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "WhatsApp Business Account id in this organization (whatsapp_accounts.id, not Meta's WABA id)."
}
}
}