Your AI Connector Docs

WhatsApp Templates API

WhatsApp message templates are pre-written messages that have been approved for sending outside the normal 24-hour conversation window — for example a welcome message, an appointment reminder, or a re-engagement nudge. This API lets you list, create, edit, submit, check, delete, and send templates programmatically.

All paths below are relative to the API base URL:

https://api.youraiconnector.com/v1

Every request must be authenticated. See Authentication for the four accepted methods. The examples on this page use the X-API-Key header (and one query-parameter form for cURL).

Note: Templates ride on the WhatsApp Business API channel, so this part of the API requires both API access and a plan that includes WhatsApp channels. Without them, requests are rejected with a 403.


Working with sub-accounts (agencies)


Approval states

Because messages sent outside an open conversation must be reviewed by WhatsApp first, every template carries an approval status:

Status Meaning
draft Created or saved but not yet sent for review. You can still edit it.
received Submitted and accepted into the review queue.
pending Under review.
approved Cleared for sending.
rejected Turned down. The rejection_reason field explains why; fix it, then submit again.

Only draft and rejected templates can be edited or (re)submitted. Once a template is approved it is locked — create a new one if you need changes.

Auto-approval: Some channels do not require an external review step. Templates created or submitted for a campaign on such a channel are stored as approved immediately, with no content ID (sid).


Templates on Meta-connected accounts

These endpoints work the same way whichever WhatsApp connection your account runs on, but what happens behind them differs:

  • On a managed WhatsApp connection, templates are registered with the messaging provider and sid is the provider’s content ID (HXXXXXXXX…).
  • On an account whose number runs on its own WhatsApp Business Account (either Meta connection option), templates are created and reviewed in that WhatsApp Business Account and sid is Meta’s own template id — a numeric string such as "3394843740694756". status still uses the values in the table above, and rejection_reason still carries Meta’s explanation.

Two extra endpoints exist for this: one to ask which connection you are on, and one to reconcile your template list with your WhatsApp Business Account. Templates that already exist in the WhatsApp Business Account are imported into your library by the sync, so a GET /whatsapp-templates afterwards lists them like any other template.

Check which connection templates run on

GET /whatsapp-templates/provider

Field Description
provider twilio when templates are registered with the managed messaging provider, meta when they live in your own WhatsApp Business Account.
lane Which Meta connection is in use — meta_cloud_api (your own Meta app) or meta_embedded (connected through our Meta app). null on a managed connection.
waba_id The WhatsApp Business Account templates are created in, or null.
templates_enabled false when the Meta connection is not finished yet (no WhatsApp Business Account or access token stored). Creating or submitting templates fails with a 400 until it is.

cURL

curl "https://api.youraiconnector.com/v1/whatsapp-templates/provider?apiKey=YOUR_API_KEY"

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates/provider", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/whatsapp-templates/provider",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()

Response

{
  "success": true,
  "provider": "meta",
  "lane": "meta_cloud_api",
  "waba_id": "2357661648036355",
  "templates_enabled": true
}

Sync templates from Meta

Refreshes the approval status of every template that lives in your WhatsApp Business Account, and imports any template that exists there but is not in your library yet. Safe to call as often as you like. On a managed connection there is nothing to sync, so the call does nothing and simply reports how many templates you have.

POST /whatsapp-templates/meta-sync

Field Description
imported Templates found in the WhatsApp Business Account that were added to your library by this call.
updated Existing templates whose status or details changed.
total Templates in your library after the sync.

cURL

curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/meta-sync?apiKey=YOUR_API_KEY"

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates/meta-sync", {
  method: "POST",
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/meta-sync",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()

Response

{
  "success": true,
  "provider": "meta",
  "imported": 2,
  "updated": 5,
  "total": 12
}

Talking to Meta directly (advanced)

If you need something the endpoints above do not expose — template headers, footers, buttons, or a fully hand-built template — /v1/meta-templates passes your request straight through to Meta’s own template API, without storing anything in your template library. It only works on accounts whose number runs on their own WhatsApp Business Account; on a managed connection every call returns 400 asking you to connect a Meta app first.

Endpoint What it does
GET /meta-templates Lists the templates on your WhatsApp Business Account with their latest status. Add ?name= to filter to one exact template name. Returns { "success": true, "templates": [...] }.
POST /meta-templates Creates a template and submits it for Meta review in one step. Requires name, language, and body (or a complete components array instead of body). Optional: variables (array of strings), category (MARKETING, UTILITY, or AUTHENTICATION), header, footer, buttons. Returns 201 with { "success": true, "template": {...} }.
DELETE /meta-templates/{name} Deletes the template by its Meta name — every language of it. Add ?hsm_id= with Meta’s template id to remove a single language instead. Returns { "success": true, "name": "..." }.

A template Meta refuses returns 400 with Meta’s own explanation in error.


List templates

Returns all templates on your account, with a lightweight summary of each.

GET /whatsapp-templates

cURL

curl "https://api.youraiconnector.com/v1/whatsapp-templates?apiKey=YOUR_API_KEY"

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/whatsapp-templates",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()

Response

{
  "success": true,
  "data": [
    {
      "id": "template_abc123",
      "name": "welcome_message",
      "status": "approved",
      "language": "en",
      "body": "Hi {{first_name}}, thanks for reaching out!"
    },
    {
      "id": "template_def456",
      "name": "appointment_reminder",
      "status": "pending",
      "language": "en",
      "body": "Hi {{first_name}}, this is a reminder about your appointment."
    }
  ]
}

Get a template

Returns the full detail of a single template, including its variables, status, and timestamps.

GET /whatsapp-templates/{templateId}

cURL

curl "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123" \
  -H "X-API-Key: YOUR_API_KEY"

JavaScript

const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()

Response

{
  "success": true,
  "template": {
    "id": "template_abc123",
    "name": "welcome_message",
    "body": "Hi {{first_name}}, thanks for reaching out!",
    "language": "en",
    "variables": ["first_name"],
    "status": "approved",
    "sid": "HXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "type": "general",
    "category": "marketing",
    "rejection_reason": null,
    "campaign_id": "campaign123",
    "date_created": "2026-06-01T10:00:00.000Z",
    "date_updated": "2026-06-02T08:30:00.000Z",
    "submitted_at": "2026-06-01T10:05:00.000Z",
    "approved_at": "2026-06-02T08:30:00.000Z"
  }
}

A template that does not exist on your account returns 404 with { "success": false, "error": "Template not found" }.


Create a template

Creates a template for a campaign’s opening message and submits it for approval in one step.

POST /whatsapp-templates

Field Required Description
campaign_id Yes The campaign the template belongs to.
name Yes A name for the template.
language Yes Language code, for example en, es, de, pt_BR, zh_CN.
body Yes The message text, up to 1024 characters.
variables No Ordered list of variable names used in the body.

Variable placeholders may be written as {{first_name}}, {first_name}, or [first_name] — they are all normalized to the double-curly form.

The result depends on the campaign’s channels:

  • WhatsApp Business API campaign: the content is sent for WhatsApp review. The response carries campaign_status (received or pending) and a template_sid.
  • A channel without an external review step: the template is stored and auto-approved (campaign_status: "approved", template_sid: null).
  • No WhatsApp channel on the campaign: nothing is created and campaign_status is not_applicable.

cURL

curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign_id": "campaign123",
    "name": "welcome_message",
    "language": "en",
    "body": "Hi {{first_name}}, thanks for reaching out!",
    "variables": ["first_name"]
  }'

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    campaign_id: "campaign123",
    name: "welcome_message",
    language: "en",
    body: "Hi {{first_name}}, thanks for reaching out!",
    variables: ["first_name"],
  }),
});
const data = await res.json();

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "campaign_id": "campaign123",
        "name": "welcome_message",
        "language": "en",
        "body": "Hi {{first_name}}, thanks for reaching out!",
        "variables": ["first_name"],
    },
)
data = res.json()

Response (submitted for review)

{
  "success": true,
  "campaign_status": "pending",
  "template_sid": "HXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}

Create a standalone template

Creates a template in your template library without tying it to a campaign’s opening message. This is the create step of the lifecycle the rest of this page follows: create it here, edit it, submit it for review, poll its status, and delete it when you no longer need it.

POST /whatsapp-templates/docs

Field Required Description
name Yes A name for the template.
language Yes Language code, for example en, es, de, pt_BR, zh_CN.
body Yes The message text, up to 1024 characters.
variables No Ordered list of variable names used in the body.
status No draft (default) stores it without submitting; submitted queues it for WhatsApp review straight away.
type No general (default) or smart_followup.
category No marketing, utility, authentication, or authentication-international.
campaign_id No Links the template to one of your campaigns.

Authentication (one-time code) templates. WhatsApp does not accept free-text authentication templates: the message body is preset by WhatsApp and the template must carry a “copy code” button. When you create a template with category: "authentication", we submit it in that fixed shape for you. Your body is kept as the preview shown in the app, but the text your contact receives is WhatsApp’s own wording (the code, a security reminder, and a 10-minute expiry note). Declare exactly one variable, for example ["code"], and pass the code when you send (see the variables field on Send a template to a contact). The code must be shorter than 15 characters.

Which create should I use? Use this one when you want a template you can edit and submit yourself. Use POST /whatsapp-templates (above) when you want to set a campaign’s opening message — that one requires campaign_id and writes straight into the campaign.

A template created as submitted is sent for WhatsApp review in the background, so check the status endpoint for the outcome rather than expecting it in the response.

cURL

curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/docs?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "welcome_message",
    "language": "en",
    "body": "Hi {{first_name}}, thanks for reaching out!",
    "variables": ["first_name"],
    "status": "draft",
    "category": "marketing"
  }'

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates/docs", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "welcome_message",
    language: "en",
    body: "Hi {{first_name}}, thanks for reaching out!",
    variables: ["first_name"],
    status: "draft",
    category: "marketing",
  }),
});
const data = await res.json();

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/docs",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "name": "welcome_message",
        "language": "en",
        "body": "Hi {{first_name}}, thanks for reaching out!",
        "variables": ["first_name"],
        "status": "draft",
        "category": "marketing",
    },
)
data = res.json()

Response

{
  "success": true,
  "template_id": "template_abc123",
  "status": "draft"
}

A missing name, language, or body, an unsupported language, a status other than draft or submitted, an unknown type or category, or a body over 1024 characters returns 400 with an explanatory error. A campaign_id that is not one of your campaigns returns 404.


Update a template

Edits a template that has not been approved yet. Only templates with status draft or rejected can be edited. Supply any combination of name, body, language, and variables — only the fields you send are changed.

PUT /whatsapp-templates/{templateId}

Editing does not resubmit the template for review. Use the submit endpoint afterwards.

cURL

curl -X PUT "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "Hi {{first_name}}, here is an update for you.",
    "variables": ["first_name"]
  }'

JavaScript

const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      body: "Hi {{first_name}}, here is an update for you.",
      variables: ["first_name"],
    }),
  }
);
const data = await res.json();

Python

import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "body": "Hi {{first_name}}, here is an update for you.",
        "variables": ["first_name"],
    },
)
data = res.json()

Response

{
  "success": true,
  "template_id": "template_abc123"
}

Trying to edit a template that is already approved (or otherwise not editable), sending no fields, or sending an invalid value returns 400 with an explanatory error.


Submit a template for approval

Submits a draft or rejected template for review. Templates on a channel that does not require external review are approved immediately; all others are sent to WhatsApp and the returned status (usually received or pending) is stored on the template.

POST /whatsapp-templates/{templateId}/submit

Follow-up templates must declare and use their required variables before they can be submitted: a first-name placeholder, plus a personal-context placeholder for smart follow-ups.

cURL

curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/submit" \
  -H "X-API-Key: YOUR_API_KEY"

JavaScript

const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/submit",
  { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/submit",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()

Response

{
  "success": true,
  "template_id": "template_abc123",
  "status": "pending",
  "sid": "HXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}

Check approval status

A lightweight endpoint for polling a template’s current status. The status is read from the stored record, which is refreshed periodically in the background, so a very recent approval or rejection can take a short while to appear.

GET /whatsapp-templates/{templateId}/status

cURL

curl "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/status" \
  -H "X-API-Key: YOUR_API_KEY"

JavaScript

const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/status",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/status",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()

Response

{
  "success": true,
  "template_id": "template_abc123",
  "name": "welcome_message",
  "status": "approved",
  "sid": "HXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "rejection_reason": null,
  "date_updated": "2026-06-02T08:30:00.000Z"
}

Delete a template

Removes the template record from your account.

DELETE /whatsapp-templates/{templateId}

Important: On a managed connection only the stored record is removed — content that WhatsApp has already approved may remain registered with the messaging provider. On an account running on its own WhatsApp Business Account the template is deleted from that account as well. Either way, if a campaign still uses this template, re-point that campaign to another template before deleting, otherwise sends that rely on it will fail.

cURL

curl -X DELETE "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123" \
  -H "X-API-Key: YOUR_API_KEY"

JavaScript

const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123",
  { method: "DELETE", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();

Python

import requests

res = requests.delete(
    "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()

Response

{
  "success": true,
  "template_id": "template_abc123",
  "note": "The template record was removed from your account. Content already approved by WhatsApp may remain registered with the messaging provider."
}

Send a template to a contact

Sends an approved template to a contact, even when there is no open conversation — this reopens the chat session. You can target the contact by contactId or by phoneNumber, and choose the template by whatsappTemplateId or by templateName.

POST /whatsapp-templates/send

Field Required Description
contactId One of these two The contact’s ID.
phoneNumber One of these two The contact’s phone number (with country code, no spaces). Looked up or created if needed.
whatsappTemplateId One of these two The template’s ID.
templateName One of these two The template’s name, as shown in the app.
firstName No Used to fill in a newly created contact.
lastName No Used to fill in a newly created contact.
email No Used to fill in a newly created contact.
variables No Explicit values for the template’s variables, keyed by variable name, for example { "code": "482913" }. A value given here wins over the contact’s fields for that variable; variables you leave out are still filled from the contact as described below. This is how you pass a one-time code to an authentication template.

The body of the template supports advanced variable substitution:

  • Basic variables: {{first_name}}, {{email}}, {{company}}
  • Default values: {{first_name|there}} shows there if the field is empty
  • Transformations: {{company|uppercase}}, {{name|lowercase}}, {{name|capitalize}}
  • Combined: {{company|Your Company|uppercase}}

Credits: Sending a template consumes credits. The exact cost depends on the recipient’s country and the template’s category.

cURL

curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/send?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contactId": "contact123",
    "whatsappTemplateId": "template_abc123"
  }'

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates/send", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    contactId: "contact123",
    whatsappTemplateId: "template_abc123",
  }),
});
const data = await res.json();

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/send",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "contactId": "contact123",
        "whatsappTemplateId": "template_abc123",
    },
)
data = res.json()

Response

{
  "success": true,
  "data": "WhatsApp template message sent successfully"
}

A request missing both a contact identifier and both template identifiers returns 400. If your account lacks the messaging credentials needed to send, the response is 403.


Create or update a campaign’s live template

A second pair of endpoints for a campaign’s opening template, scoped by path instead of by a campaign_id in the body. These are the ones to use for a campaign that is already live: unlike Create a template above, updating here also re-submits the campaign’s follow-up drafts for review, so the opening template and its follow-ups stay in sync.

POST /whatsapp-templates/campaign/{campaignId} creates the campaign’s opening template. PUT /whatsapp-templates/campaign/{campaignId} edits it — the campaign must already have a template, or this returns 400.

Field Required Description
name Yes A name for the template.
language Yes Language code, for example en, es, de, pt_BR, zh_CN.
body Yes The message text, up to 1024 characters.
variables Yes Ordered list of variable names used in the body. Pass an empty array if the template uses none.

cURL (create)

curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/campaign/campaign123?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "welcome_message",
    "language": "en",
    "body": "Hi {{first_name}}, thanks for reaching out!",
    "variables": ["first_name"]
  }'

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates/campaign/campaign123", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "welcome_message",
    language: "en",
    body: "Hi {{first_name}}, thanks for reaching out!",
    variables: ["first_name"],
  }),
});
const data = await res.json();

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/campaign/campaign123",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "name": "welcome_message",
        "language": "en",
        "body": "Hi {{first_name}}, thanks for reaching out!",
        "variables": ["first_name"],
    },
)
data = res.json()

Response

{
  "success": true,
  "campaign_status": "pending",
  "template_sid": "HXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "message": "WhatsApp template created and campaign updated successfully."
}

To edit, swap the method to PUT and the same fields — this re-submits the opening template (and the campaign’s follow-up drafts, on a WhatsApp API campaign) for review.

A campaign that does not belong to your account returns 404; a campaign belonging to another account you’re not authorized for returns 403. Editing a campaign with no existing template returns 400.


Send a template to an existing contact

A simpler, path-scoped alternative to Send a template to a contact above: both the template and the contact must already exist — nothing is looked up by name or created on the fly.

POST /whatsapp-templates/{templateId}/send-to-contact

Field Required Description
contactId Yes The contact’s ID. Must belong to your account.

cURL

curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/send-to-contact?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "contactId": "contact123" }'

JavaScript

const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/send-to-contact",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ contactId: "contact123" }),
  }
);
const data = await res.json();

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/send-to-contact",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"contactId": "contact123"},
)
data = res.json()

Response

{
  "success": true,
  "data": "WhatsApp template message sent successfully"
}

Credits: Sending consumes credits, priced the same way as the endpoint above. A contactId that is missing or not on your account returns 403; a templateId that does not exist returns 404.


Bulk-send a template

Send one template to many contacts in a single call, with a cost preview you can show before committing.

Estimate the cost first

Returns what sending would cost, broken down by destination country, without sending anything or moving any credits. Template pricing is per destination country, so this has to be computed server-side against the real contacts rather than estimated client-side.

POST /whatsapp-templates/{templateId}/estimate-bulk-cost

Field Required Description
contactIds Yes Contacts to price, up to 500 per call. Duplicates are counted once.

cURL

curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/estimate-bulk-cost?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "contactIds": ["contact123", "contact456"] }'

JavaScript

const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/estimate-bulk-cost",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ contactIds: ["contact123", "contact456"] }),
  }
);
const data = await res.json();

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/estimate-bulk-cost",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"contactIds": ["contact123", "contact456"]},
)
data = res.json()

Response

{
  "success": true,
  "data": {
    "countries": [
      {
        "countryCode": "1",
        "name": "United States",
        "iso": "US",
        "flag": "🇺🇸",
        "contactCount": 120,
        "costPerContact": 0.5,
        "subtotal": 60.0
      }
    ],
    "totalContacts": 120,
    "totalTemplateCost": 60.0,
    "templateCategory": "marketing",
    "skippedContacts": 2
  }
}

skippedContacts counts ids that were missing, not yours, or held no phone number — the estimate covers only the rest, so a non-zero value means the real send will reach fewer contacts than you selected.

Send the batch

Sends the template to every contact in the list, resolving any smart variables per contact and charging credits per send.

POST /whatsapp-templates/{templateId}/bulk-send

Field Required Description
contactIds Yes Contacts to send to, up to 5000 per call.

cURL

curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/bulk-send?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "contactIds": ["contact123", "contact456"] }'

JavaScript

const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/bulk-send",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ contactIds: ["contact123", "contact456"] }),
  }
);
const data = await res.json();

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/template_abc123/bulk-send",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"contactIds": ["contact123", "contact456"]},
)
data = res.json()

Response

{
  "success": true,
  "data": { "sent": 118, "failed": 2, "total": 120 }
}

A contact that fails (not found, not on your account, or a send error) is skipped and counted in failed rather than stopping the batch. An empty contactIds, more than 5000 ids on a send (500 on an estimate), or a missing templateId returns 400.


Retry a failed message

Two endpoints for resending a message that failed, without creating a new message record or spending credits again.

POST /whatsapp-templates/messages/{contactId}/{messageId}/retry-template retries a failed template message specifically — it re-resolves the template content from the campaign if the failed message doesn’t already carry it. Only messages with status failed and type template can be retried this way.

POST /whatsapp-templates/messages/{contactId}/{messageId}/retry is channel-agnostic and works for any failed non-template message (for example WhatsApp Web), dispatching to the right send path based on the message’s channel. It accepts status failed, failed_connection, limit_exceeded, or queued_retry.

Neither endpoint takes a request body.

cURL

curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/messages/contact123/msg_abc789/retry-template?apiKey=YOUR_API_KEY"

JavaScript

const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/messages/contact123/msg_abc789/retry-template",
  { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/messages/contact123/msg_abc789/retry-template",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()

Response

{
  "success": true,
  "data": "Message retry initiated successfully"
}

For the channel-agnostic version, swap the path to .../msg_abc789/retry. A message whose status isn’t eligible for retry, or (on the template endpoint) that isn’t a template message, returns 400. A missing contact or message returns 404.


WhatsApp Business profile

Manage the WhatsApp Business profile (about, address, description, email, websites, business category, and logo) shown to contacts on WhatsApp. Works on both a managed connection and an account running its own WhatsApp Business Account.

Save the profile

PUT /whatsapp-templates/profile

Field Required Description
phoneNumber Yes The WhatsApp number this profile belongs to. Must be connected on your account.
about No Short “About” text shown on the profile.
address No Business address.
description No Longer business description.
email No Contact email shown on the profile.
websites No Array of website URLs. Each must be a valid URL.
vertical No Business category, for example Retail or Professional Services.
profilePictureHandle No The handle returned by the picture-upload endpoint below, to set the profile photo.

cURL

curl -X PUT "https://api.youraiconnector.com/v1/whatsapp-templates/profile?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumber": "+31612345678",
    "about": "We reply within a few hours",
    "email": "support@example.com",
    "websites": ["https://example.com"]
  }'

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates/profile", {
  method: "PUT",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    phoneNumber: "+31612345678",
    about: "We reply within a few hours",
    email: "support@example.com",
    websites: ["https://example.com"],
  }),
});
const data = await res.json();

Python

import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/whatsapp-templates/profile",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "phoneNumber": "+31612345678",
        "about": "We reply within a few hours",
        "email": "support@example.com",
        "websites": ["https://example.com"],
    },
)
data = res.json()

Response

{
  "success": true,
  "data": "WhatsApp Business profile updated successfully"
}

A missing phoneNumber, an invalid website URL, or a phoneNumber not connected on your account returns 400 or 404.

Upload a profile picture

Downloads an image from a URL you provide and uploads it to WhatsApp, returning a handle. Pass that handle as profilePictureHandle on the save-profile call above to set it as the photo — this endpoint only uploads the image, it does not set it by itself.

POST /whatsapp-templates/profile/picture

Field Required Description
phoneNumber Yes The WhatsApp number this profile belongs to.
fileUrl Yes A publicly reachable URL to the image to upload.

cURL

curl -X POST "https://api.youraiconnector.com/v1/whatsapp-templates/profile/picture?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumber": "+31612345678",
    "fileUrl": "https://example.com/logo.png"
  }'

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/whatsapp-templates/profile/picture", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    phoneNumber: "+31612345678",
    fileUrl: "https://example.com/logo.png",
  }),
});
const data = await res.json();

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/whatsapp-templates/profile/picture",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "phoneNumber": "+31612345678",
        "fileUrl": "https://example.com/logo.png",
    },
)
data = res.json()

Response

{
  "success": true,
  "data": "1234567890123456"
}

data is the uploaded picture’s handle. A missing phoneNumber or fileUrl, or a phoneNumber with no WhatsApp access token on file, returns 400; an unreachable or invalid fileUrl returns an error describing why the download failed.


Check a sender’s status

Polls (and refreshes) a connected WhatsApp number’s live sending status with the messaging provider. Useful for confirming a number is actually able to send before you rely on it.

GET /whatsapp-templates/sender-status/{phoneNumber}

cURL

curl "https://api.youraiconnector.com/v1/whatsapp-templates/sender-status/+31612345678" \
  -H "X-API-Key: YOUR_API_KEY"

JavaScript

const res = await fetch(
  "https://api.youraiconnector.com/v1/whatsapp-templates/sender-status/+31612345678",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/whatsapp-templates/sender-status/+31612345678",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()

Response

{
  "success": true,
  "data": "ONLINE"
}

data is one of ONLINE (sending normally), PENDING (still being verified), or DELETED (the provider no longer recognizes this sender — reconnect the number). A phoneNumber with no WhatsApp business information on file returns 404.


Generate follow-up templates with AI

The platform can write a campaign’s WhatsApp follow-up templates for you — the nudges sent when a conversation goes quiet — from the campaign’s own instructions and goal. There is one job endpoint that runs in the background, plus three older endpoints kept for existing integrations. All of them use AI credits.

Start a generation job

POST /campaigns/{campaignId}/template-generation

Field Required Description
type No all (the default) writes the whole follow-up set. cold_only writes only the messages for contacts who never replied.

cURL

curl -X POST "https://api.youraiconnector.com/v1/campaigns/campaign_abc123/template-generation?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "type": "all" }'

JavaScript

const res = await fetch(
  "https://api.youraiconnector.com/v1/campaigns/campaign_abc123/template-generation",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ type: "all" }),
  }
);
const data = await res.json();

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/campaigns/campaign_abc123/template-generation",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"type": "all"},
)
data = res.json()

Response (202)

{ "success": true, "campaign_id": "campaign_abc123", "type": "all" }

The call returns as soon as the job is queued. Read the campaign (GET /campaigns/{campaignId}, see the Campaigns API) and watch its template_generation_status object until it finishes:

Field Description
status processing while the job runs, then completed or failed.
progress 0 to 100.
current_template, total_templates How many templates have been written so far, out of how many the job will write — 11 for an outgoing or combined campaign, 9 otherwise.
error Why a failed job stopped, for example not enough credits.
started_at, completed_at When the job began and ended.

The generated templates land on the campaign like any other, so they show up in List templates and still go through WhatsApp approval before they can be sent. A 400 means type was something other than all or cold_only; a 404 means the campaign does not exist or belongs to another account.

Agents have a twin of this call, POST /agents/{agentId}/template-generation, which writes the follow-ups for an Agent and finishes during the call in the usual case — see Generate follow-up messages in the AI Agents API.

The older generation endpoints

Three earlier endpoints do the same work and are kept so existing integrations keep running. New code should use the job endpoint above.

Endpoint What it does
POST /whatsapp-templates/campaign/{campaignId}/generate-async Starts follow-up generation for the campaign in the background and returns 202 with { "success": true, "data": { "result": "success", "message": "..." } }. Credits are charged up front (skipped on an account that brings its own AI key) and the campaign’s template_generation_status reports progress exactly as above.
POST /whatsapp-templates/campaign/{campaignId}/generate-followups Generates all nine follow-up templates during the call — for a campaign created before automatic follow-ups existed, or one that needs them written again — and returns 200 with templatesGenerated inside data.
POST /whatsapp-templates/agent/{agentId}/generate-followups The same synchronous generation addressed by Agent. The response adds agent_id, campaign_id and target: "campaign" when the templates were written onto the Agent’s campaign, "agent" (with campaign_id: null) when the Agent has no campaign and they were stored on the Agent itself. A missing or foreign Agent is a 404.

All three need automatic follow-ups on the account and enough credits — a 400 names which is missing — and the campaign-addressed pair return 403 when the campaign belongs to another account.


Templates API errors

Template endpoints return the standard error envelope:

{
  "success": false,
  "error": "Template not found"
}

A 404 on these endpoints usually means the resource wasn’t found — either it doesn’t exist or it belongs to another account. A few endpoints (the campaign-scoped create/update, and sends to an existing contact) return 403 instead when the campaign or contact belongs to someone else rather than not existing at all. Some endpoints also include an error_code field mirroring the HTTP status. The shared codes every endpoint can return — 400, 401, 403 (your plan does not include API access), 429 (rate limit) and 500 — are listed with retry guidance in Errors & Pagination.


Next steps