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.

Authentication: session token
These are dashboard endpoints. Send the access token of a signed-in Inboxi user as 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

GET/api/whatsapp/templatesTemplates of your workspace, newest first
ParameterTypeRequiredDescription
statusstringoptionalAPPROVED, PENDING, REJECTED, PAUSED, DISABLED or DELETED. Deleted templates are hidden unless you ask for them.
categorystringoptionalUTILITY, MARKETING or AUTHENTICATION.
bizCategorystringoptionalBusiness category, see below.
languagestringoptionalLanguage code, for example ar, en, en_US.
searchstringoptionalPartial match on the template name.
chatIdnumberoptionalOnly templates of the WhatsApp number this conversation uses.
connectionIdnumberoptionalOnly templates of this WhatsApp number (connection).
groupBystringoptionalSet 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"
json
{
  "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

json
{
  "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"
}
FieldTypeRequiredDescription
componentsarrayoptionalMeta's component list: HEADER, BODY, FOOTER, BUTTONS. Body variables are numbered {{1}}, {{2}}…
variablesSchemaarrayoptionalInboxi's labels for each numbered variable, used by the dashboard and by event mapping.
scopestringoptionalorg: kept on every connected number. connection: pinned to one number.
isDefaultbooleanoptionalCreated from Inboxi's default catalog (create-defaults).

Business categories (bizCategory)

bizCategoryLabelDefault templates
storeStore10
restaurantRestaurant8
clinicClinic8
educationEducation8
salonSalon & Beauty6
gymGym & Fitness6
realestateReal Estate6
hotelHotel & Travel6
autoAuto & Repair6
servicesServices6
generalGeneral6

Button types

A template can have up to 3 buttons.

TypeBehaviorStructure
QUICK_REPLYSends the button text back as a reply.{ "type": "QUICK_REPLY", "text": "Confirm" }
URLOpens 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

POST/api/whatsapp/templates/:id/sendSend an approved template to a phone number
ParameterTypeRequiredDescription
tostringrequiredRecipient with country code, for example +201012345678.
variablesstring[]optionalBody values in order. The count must match the number of {{n}} in the body.
buttonPayloadsstring[]optionalIndexed by button position. QUICK_REPLY: custom payload. URL with {{1}}: the dynamic suffix. Leave a position empty for static buttons.
chatIdnumberoptionalConversation to attach the message to. It also selects the WhatsApp number that conversation uses.
connectionIdnumberoptionalSend from this WhatsApp number (connection) instead.
headerMediaobjectoptionalFor 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"
    ]
  }'
json
{
  "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

StatusReason
400Recipient phone number (to) is required
422Connect a WhatsApp number first to send message templates.
500Template not found, not APPROVED, or wrong variable count (for example expects 3 variable(s), received 2).
4xx/5xxRejected 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

POST/api/whatsapp/templatesSubmit a new template to Meta for approval
ParameterTypeRequiredDescription
namestringrequiredLowercase letters, digits and underscores, starting with a letter, for example order_confirm.
componentsarrayrequiredHEADER, BODY, FOOTER, BUTTONS in Meta's format. Ignored for AUTHENTICATION.
languagestringoptionalDefault ar. For example en, en_US.
categorystringoptionalUTILITY (default), MARKETING or AUTHENTICATION.
variablesSchemaarrayoptionalLabels for the body variables: [{ index, key, label, sample }].
bizCategorystringoptionalBusiness category for grouping.
scopestringoptionalorg (default): created on every connected number and future ones. connection: one number only.
platformConnectionIdnumberoptionalNumber 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

PUT/api/whatsapp/templates/:idEdit components, category, variables or bizCategory

Editing 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

POST/api/whatsapp/templates/ai-generateDraft a template from a description (1 AI credit)
ParameterTypeRequiredDescription
promptstringrequiredWhat the template is for, in plain language.
languagestringoptionalar (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"
  }'
json
{
  "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"]
  }
}
The result is a draft: nothing is submitted to Meta. Review it, convert it to components, then call Create a template. Out of credits returns 402 with "code": "INSUFFICIENT_CREDITS"; an empty prompt returns 400.

Sync statuses from Meta

POST/api/whatsapp/templates/syncPull the latest templates and statuses from Meta
curl -X POST "https://inboxi.app/api/whatsapp/templates/sync" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
json
{
  "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

POST/api/whatsapp/templates/create-defaultsSubmit Inboxi's ready-made templates to Meta

The 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.

ParameterTypeRequiredDescription
bizCategoryFilterstring[]optionalSubmit exactly these categories instead, for example ["restaurant"].
requireBusinessTypebooleanoptionalDo nothing if the workspace has no business type set.
platformConnectionIdnumberoptionalWhatsApp 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"
    ]
  }'
json
{
  "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

GET/api/whatsapp/templates/categoriesLabels, icons and default-template counts
curl "https://inboxi.app/api/whatsapp/templates/categories" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
json
{
  "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

DELETE/api/whatsapp/templates/:idDelete from Meta and mark as deleted in Inboxi
curl -X DELETE "https://inboxi.app/api/whatsapp/templates/42" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
json
{ "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.