API Reference · Endpoints
WhatsApp templates
List, create, generate, sync, send and delete Meta-approved WhatsApp message templates.
Templates are message formats pre-approved by Meta. They are the only way to start a WhatsApp conversation or to message a customer outside the 24-hour window. Each template has a Meta category (UTILITY, MARKETING or AUTHENTICATION), a business category (bizCategory), body variables, and optional buttons. For the dashboard side see WhatsApp templates.
Authorization: Bearer …. Your X-API-Key is not accepted here. To send a template from a server integration without a user session, trigger an event with a template attached instead.List templates
/api/whatsapp/templatesTemplates of your workspace, newest first| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | optional | APPROVED, PENDING, REJECTED, PAUSED, DISABLED or DELETED. Deleted templates are hidden unless you ask for them. |
category | string | optional | UTILITY, MARKETING or AUTHENTICATION. |
bizCategory | string | optional | Business category, see below. |
language | string | optional | Language code, for example ar, en, en_US. |
search | string | optional | Partial match on the template name. |
chatId | number | optional | Only templates of the WhatsApp number this conversation uses. |
connectionId | number | optional | Only templates of this WhatsApp number (connection). |
groupBy | string | optional | Set to template to get one entry per template name and language, with a per-number breakdown, instead of one row per number. |
curl "https://inboxi.app/api/whatsapp/templates?bizCategory=store&status=APPROVED" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"{
"success": true,
"data": [ { "id": 42, "name": "order_confirm", "status": "APPROVED", ... } ],
"scopedConnectionId": null,
"mode": "flat"
}If your workspace has several WhatsApp numbers, the default (flat) mode returns one row per number. With groupBy=template the response has "mode": "grouped" and each entry carries a wabas array (status per number) and a summary of how many numbers approved it.
The template object
{
"id": 42,
"platformConnectionId": 3,
"metaTemplateId": "1234567890",
"name": "order_confirm",
"language": "en",
"category": "UTILITY",
"bizCategory": "store",
"status": "APPROVED",
"qualityScore": null,
"rejectedReason": null,
"isDefault": true,
"scope": "org",
"components": [
{
"type": "BODY",
"text": "Hi {{1}}, your order #{{2}} for {{3}} is confirmed.",
"example": { "body_text": [["Ahmed", "ORD-5821", "150 EGP"]] }
},
{ "type": "FOOTER", "text": "Thanks for shopping with us" },
{
"type": "BUTTONS",
"buttons": [
{ "type": "QUICK_REPLY", "text": "Confirm order" },
{ "type": "URL", "text": "Track order", "url": "https://site.com/track/{{1}}" }
]
}
],
"variablesSchema": [
{ "index": 1, "key": "customer_name", "label": "Customer name", "sample": "Ahmed" },
{ "index": 2, "key": "order_number", "label": "Order number", "sample": "ORD-5821" },
{ "index": 3, "key": "total_amount", "label": "Total", "sample": "150 EGP" }
],
"lastSyncedAt": "2026-03-18T12:00:00.000Z"
}| Field | Type | Required | Description |
|---|---|---|---|
components | array | optional | Meta's component list: HEADER, BODY, FOOTER, BUTTONS. Body variables are numbered {{1}}, {{2}}… |
variablesSchema | array | optional | Inboxi's labels for each numbered variable, used by the dashboard and by event mapping. |
scope | string | optional | org: kept on every connected number. connection: pinned to one number. |
isDefault | boolean | optional | Created from Inboxi's default catalog (create-defaults). |
Business categories (bizCategory)
| bizCategory | Label | Default templates |
|---|---|---|
store | Store | 10 |
restaurant | Restaurant | 8 |
clinic | Clinic | 8 |
education | Education | 8 |
salon | Salon & Beauty | 6 |
gym | Gym & Fitness | 6 |
realestate | Real Estate | 6 |
hotel | Hotel & Travel | 6 |
auto | Auto & Repair | 6 |
services | Services | 6 |
general | General | 6 |
Button types
A template can have up to 3 buttons.
| Type | Behavior | Structure |
|---|---|---|
QUICK_REPLY | Sends the button text back as a reply. | { "type": "QUICK_REPLY", "text": "Confirm" } |
URL | Opens a link. A {{1}} at the end of the URL makes it dynamic. | { "type": "URL", "text": "View", "url": "https://site.com/{{1}}" } |
A dynamic URL button gets its suffix at send time: with https://site.com/report/{{1}} and the value RPT-5821, the customer opens https://site.com/report/RPT-5821. Authentication templates use a copy-code button that Inboxi fills with the code automatically.
Send a template
/api/whatsapp/templates/:id/sendSend an approved template to a phone number| Parameter | Type | Required | Description |
|---|---|---|---|
to | string | required | Recipient with country code, for example +201012345678. |
variables | string[] | optional | Body values in order. The count must match the number of {{n}} in the body. |
buttonPayloads | string[] | optional | Indexed by button position. QUICK_REPLY: custom payload. URL with {{1}}: the dynamic suffix. Leave a position empty for static buttons. |
chatId | number | optional | Conversation to attach the message to. It also selects the WhatsApp number that conversation uses. |
connectionId | number | optional | Send from this WhatsApp number (connection) instead. |
headerMedia | object | optional | For templates with an image, video or document header: { "link": "https://…", "filename": "invoice.pdf" } (filename for documents only). |
curl -X POST "https://inboxi.app/api/whatsapp/templates/42/send" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"to": "+201012345678",
"variables": [
"Ahmed",
"ORD-5821",
"150 EGP"
],
"buttonPayloads": [
"confirm_order",
"ORD-5821"
]
}'{
"success": true,
"data": {
"wamid": "wamid.HBgNMjAxMDE2NzcyMTE4…",
"templateName": "order_confirm",
"renderedBody": "Hi Ahmed, your order #ORD-5821 for 150 EGP is confirmed.",
"chatId": 42,
"sentViaConnectionId": 3,
"sentViaPhoneNumberId": "…"
}
}Errors
| Status | Reason |
|---|---|
| 400 | Recipient phone number (to) is required |
| 422 | Connect a WhatsApp number first to send message templates. |
| 500 | Template not found, not APPROVED, or wrong variable count (for example expects 3 variable(s), received 2). |
| 4xx/5xx | Rejected by Meta. The response includes metaCode, metaSubcode and metaDetails. |
Poll the delivery of a sent template with GET /api/whatsapp/templates/status/:wamid, which returns status (sent, delivered, read, failed or pending), errorCode, errorMessage and timestamp.
Create a template
/api/whatsapp/templatesSubmit a new template to Meta for approval| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | required | Lowercase letters, digits and underscores, starting with a letter, for example order_confirm. |
components | array | required | HEADER, BODY, FOOTER, BUTTONS in Meta's format. Ignored for AUTHENTICATION. |
language | string | optional | Default ar. For example en, en_US. |
category | string | optional | UTILITY (default), MARKETING or AUTHENTICATION. |
variablesSchema | array | optional | Labels for the body variables: [{ index, key, label, sample }]. |
bizCategory | string | optional | Business category for grouping. |
scope | string | optional | org (default): created on every connected number and future ones. connection: one number only. |
platformConnectionId | number | optional | Number to create it on first. Required when scope is connection. |
curl -X POST "https://inboxi.app/api/whatsapp/templates" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "exam_report",
"language": "en",
"category": "UTILITY",
"bizCategory": "education",
"components": [
{
"type": "BODY",
"text": "Hello {{1}}, the exam report for {{2}} is ready.",
"example": {
"body_text": [
[
"Ahmed",
"Mohamed"
]
]
}
},
{
"type": "BUTTONS",
"buttons": [
{
"type": "URL",
"text": "View report",
"url": "https://school.com/report/{{1}}"
}
]
}
],
"variablesSchema": [
{
"index": 1,
"key": "customer_name",
"label": "Parent name",
"sample": "Ahmed"
},
{
"index": 2,
"key": "student_name",
"label": "Student name",
"sample": "Mohamed"
}
]
}'Returns the stored template in data, usually with "status": "PENDING" until Meta reviews it. Run sync later to pick up the decision.
Authentication (OTP) templates
Meta writes the text of authentication templates itself. With "category": "AUTHENTICATION", Inboxi ignores components and builds them from these optional fields: codeExpirationMinutes (1-90), otpType (COPY_CODE default, ONE_TAP, ZERO_TAP) and addSecurityRecommendation (default true).
Edit a template
/api/whatsapp/templates/:idEdit components, category, variables or bizCategoryEditing sends the template back to Meta for review (status PENDING) and changes only that number's copy. Call POST /api/whatsapp/templates/:id/push-to-all to copy the edit to your other numbers.
Generate a template with AI
/api/whatsapp/templates/ai-generateDraft a template from a description (1 AI credit)| Parameter | Type | Required | Description |
|---|---|---|---|
prompt | string | required | What the template is for, in plain language. |
language | string | optional | ar (default) or en_US. |
curl -X POST "https://inboxi.app/api/whatsapp/templates/ai-generate" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"prompt": "appointment reminder for clinic patients with a link to reschedule",
"language": "en_US"
}'{
"success": true,
"data": {
"name": "clinic_appointment_reminder",
"category": "UTILITY",
"bizCategory": "clinic",
"bodyText": "Hi {{customer_name}} 👋\nThis is a reminder of your clinic appointment.\nUse the buttons below to confirm or cancel.",
"footerText": "Health Clinic",
"buttons": ["Confirm", "Cancel"]
}
}components, then call Create a template. Out of credits returns 402 with "code": "INSUFFICIENT_CREDITS"; an empty prompt returns 400.Sync statuses from Meta
/api/whatsapp/templates/syncPull the latest templates and statuses from Metacurl -X POST "https://inboxi.app/api/whatsapp/templates/sync" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"{
"success": true,
"synced": 24,
"created": 2,
"updated": 5,
"wabas": [ ... ],
"catchUp": { ... }
}Syncs every connected WhatsApp number. Fails with 500 "No active WhatsApp Cloud connection" if none is connected.
Create the default templates
/api/whatsapp/templates/create-defaultsSubmit Inboxi's ready-made templates to MetaThe catalog holds 76 templates across 11 business categories. Which ones are submitted depends on the workspace's business type: its category plus the general set. With no business type, only the 6 general templates are submitted.
| Parameter | Type | Required | Description |
|---|---|---|---|
bizCategoryFilter | string[] | optional | Submit exactly these categories instead, for example ["restaurant"]. |
requireBusinessType | boolean | optional | Do nothing if the workspace has no business type set. |
platformConnectionId | number | optional | WhatsApp number to create them on. |
curl -X POST "https://inboxi.app/api/whatsapp/templates/create-defaults" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"bizCategoryFilter": [
"clinic"
]
}'{
"success": true,
"data": [
{ "name": "appointment_confirm", "status": "created", "metaId": "…" },
{ "name": "welcome_message", "status": "already_exists" },
{ "name": "promo_offer", "status": "error", "error": "…", "code": 100, "subcode": null }
]
}With requireBusinessType and no type set, data is { "skipped": true, "reason": "business_type_not_set", … }.
List business categories
/api/whatsapp/templates/categoriesLabels, icons and default-template countscurl "https://inboxi.app/api/whatsapp/templates/categories" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"{
"success": true,
"data": {
"store": { "labelAr": "متجر", "labelEn": "Store", "icon": "🛍️" },
"clinic": { "labelAr": "عيادة / طبيب", "labelEn": "Clinic", "icon": "🏥" },
...
},
"counts": { "store": 10, "restaurant": 8, "clinic": 8, ... }
}Related: GET /api/whatsapp/templates/library returns the full default catalog grouped by category with an alreadyDeployed flag per template, and GET/PATCH /api/whatsapp/templates/business-type reads or sets the workspace's business type.
Delete a template
/api/whatsapp/templates/:idDelete from Meta and mark as deleted in Inboxicurl -X DELETE "https://inboxi.app/api/whatsapp/templates/42" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"{ "success": true }The template is removed at Meta by name and kept in Inboxi with status DELETED (visible with ?status=DELETED). Meta does not let you reuse a deleted template's name right away.