API reference
Templates API
Listing templates and submitting your own for Meta approval.
Three endpoints, for templates you author yourself. For the 18 e-commerce events, POST /v1/notifications/templates/submit submits the whole catalog in one call and needs none of this.
Listing templates
Lists templates for a number, read live from Meta. If Meta is unreachable the local mirror is returned instead, so this endpoint stays useful during a Graph API outage, at the cost of approval statuses that may be a little stale.
Query parameters
| Field | Required | Type | Description |
|---|---|---|---|
| phoneNumberId | Yes | string | Meta phone number id from GET /v1/phones. There is no default here.Example: 1030974986765331 |
curl "https://whatsapp-api.growcord.in/api/v1/templates?phoneNumberId=1030974986765331" \
-H "Authorization: Bearer sk_live_xxx"Response 200, array of
| Field | Required | Type | Description |
|---|---|---|---|
| id | No | string | null | Meta's global template id. null when the row came from the local fallback and was never assigned one. |
| name | Yes | string | Template name. |
| language | Yes | string | Locale, e.g. en_US. |
| category | Yes | string | MARKETING, UTILITY or AUTHENTICATION. |
| status | Yes | string | APPROVED, PENDING or REJECTED. Only APPROVED can be sent. |
[
{
"id": "1234567890123456",
"name": "order_confirmation",
"language": "en_US",
"category": "UTILITY",
"status": "APPROVED"
}
]Errors. 400 when the query parameter is absent:
{ "error": "`phoneNumberId` query param is required" }404 when the number is not on this account or has no access token:
{ "error": "Phone number not found or not configured" }One template, with its components
Fetches one template by its Meta id, including the component array, which is what you need to build a correct components payload for POST /v1/messages/send.
Path parameters
| Field | Required | Type | Description |
|---|---|---|---|
| id | Yes | string | Meta's global template id, the id from GET /v1/templates, not the template name. |
Query parameters
| Field | Required | Type | Description |
|---|---|---|---|
| phoneNumberId | No | string | Which number's access token resolves the lookup. Default: the account's newest number with an access token |
curl "https://whatsapp-api.growcord.in/api/v1/templates/1234567890123456?phoneNumberId=1030974986765331" \
-H "Authorization: Bearer sk_live_xxx"Response 200
| Field | Required | Type | Description |
|---|---|---|---|
| id | Yes | string | Meta template id. |
| name | Yes | string | Template name. |
| language | Yes | string | Locale. |
| category | Yes | string | Template category. |
| status | Yes | string | Approval status. |
| components | Yes | array | Meta's component array, header, body, footer and buttons. |
{
"id": "1234567890123456",
"name": "order_confirmation",
"language": "en_US",
"category": "UTILITY",
"status": "APPROVED",
"components": [
{ "type": "BODY", "text": "Hi {{1}}, your order *{{2}}* is confirmed!" }
]
}Errors. Both are 404. No configured number to resolve the lookup with:
{ "error": "No configured phone number" }Or Meta does not know that id, or this number's token cannot see it:
{ "error": "Template not found" }Submitting a template
Submits a template to Meta for approval and mirrors it locally.
Body parameters
| Field | Required | Type | Description |
|---|---|---|---|
| phoneNumberId | Yes | string | Meta phone number id from GET /v1/phones. The template is created under this number's WABA. |
| name | Yes | string | Lowercase snake_case only. It must match ^[a-z0-9_]+$, minimum 1 character. Uppercase letters, spaces and hyphens are rejected before the request reaches Meta.Example: welcome_message |
| language | No | string | Locale code. Default: en_US |
| category | Yes | string | Exactly one of MARKETING, UTILITY or AUTHENTICATION, uppercase. Anything else fails validation. |
| components | Yes | array | Meta component objects. At least one. See the placeholder rule below. |
{
"phoneNumberId": "1030974986765331",
"name": "welcome_message",
"language": "en_US",
"category": "UTILITY",
"components": [
{
"type": "BODY",
"text": "Hi {{1}}, welcome to {{2}}!",
"example": { "body_text": [["Alex", "Acme"]] }
},
{ "type": "FOOTER", "text": "Reply STOP to unsubscribe" }
]
}Response 201
| Field | Required | Type | Description |
|---|---|---|---|
| id | Yes | string | Meta's global template id. |
| name | Yes | string | Template name. |
| language | Yes | string | Locale. |
| category | Yes | string | The category Meta assigned, which is not always the one you asked for, see below. |
| status | Yes | string | Usually PENDING. |
{
"id": "1234567890123456",
"name": "welcome_message",
"language": "en_US",
"category": "UTILITY",
"status": "PENDING"
}Poll GET /v1/templates?phoneNumberId=… until status becomes APPROVED.
Errors
400, validation failure. error is a Zod object:
{
"error": {
"formErrors": [],
"fieldErrors": { "name": ["name must be lowercase snake_case (a-z, 0-9, _)"] }
}
}400, Meta refused the template. Note the status: a Meta refusal here is 400, not 502, unlike a failed send. Do not retry it. The submission is wrong, not the network:
{ "error": "Template submission failed: Template name already exists" }The usual causes are a name that already exists in the WABA, a missing or mis-sized example block, and content that breaks Meta's template policy.
404. The number is not on this account or has no access token:
{ "error": "Phone number not found or not configured" }