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
approvedimmediately, 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
sidis 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
sidis Meta’s own template id — a numeric string such as"3394843740694756".statusstill uses the values in the table above, andrejection_reasonstill 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(receivedorpending) and atemplate_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_statusisnot_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. Yourbodyis 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 thevariablesfield 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 requirescampaign_idand 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}}showsthereif 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
contactIdthat is missing or not on your account returns403; atemplateIdthat does not exist returns404.
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
- Authentication — the four ways to authenticate a request.
- Errors & Rate Limits — status codes and the 300 req/min limit.
- Campaigns API — manage the campaigns templates are attached to.