curl --request POST \
--url https://app.thinnest.ai/api/v1/campaigns \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"kind": "whatsapp",
"name": "Diwali sale — VIP customers",
"template": "diwali_offer_v2",
"language": "en",
"variables": [
{
"position": 1,
"source": "contact_name"
},
{
"position": 2,
"source": "fixed",
"value": "FESTIVE20"
}
],
"tags": [
"vip"
],
"launch": true
}
'{
"id": "cmp_9a3b7c21-4d5e-4f60-8a1b-2c3d4e5f6a7b",
"agent": "ag_5c4a5f93-2b1e-4c0a-9d3f-7e8a1b2c3d4e",
"name": "Diwali sale — VIP customers",
"kind": "whatsapp",
"status": "sending",
"template": "diwali_offer_v2",
"purpose": null,
"counts": {
"recipients": 412,
"sent": 0,
"delivered": 0,
"read": 0,
"replied": 0,
"failed": 0,
"skipped": 0
},
"scheduledAt": null,
"startedAt": "2026-10-06T10:00:02.311Z",
"finishedAt": null,
"createdAt": "2026-10-06T10:00:01.874Z",
"lastError": null,
"message": "Sending started. Messages go out in the background."
}Create Campaign
Creates a WhatsApp broadcast (kind: "whatsapp") or a calling campaign (kind: "voice") as a draft, with the same checks as the console’s builder: WhatsApp connected and the template approved with every blank filled, or for calls an agent with a number that answers phone calls and is licensed for this kind of call. The audience is built from your contacts right away, by consent; people who asked not to be contacted are always left out. Send launch: true to start it at once, or scheduledAt to start it later; a launch that is refused (no paid plan, not enough balance) still leaves the draft and says why in message, and an audience of nobody is never launched. Needs a full key.
curl --request POST \
--url https://app.thinnest.ai/api/v1/campaigns \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"kind": "whatsapp",
"name": "Diwali sale — VIP customers",
"template": "diwali_offer_v2",
"language": "en",
"variables": [
{
"position": 1,
"source": "contact_name"
},
{
"position": 2,
"source": "fixed",
"value": "FESTIVE20"
}
],
"tags": [
"vip"
],
"launch": true
}
'{
"id": "cmp_9a3b7c21-4d5e-4f60-8a1b-2c3d4e5f6a7b",
"agent": "ag_5c4a5f93-2b1e-4c0a-9d3f-7e8a1b2c3d4e",
"name": "Diwali sale — VIP customers",
"kind": "whatsapp",
"status": "sending",
"template": "diwali_offer_v2",
"purpose": null,
"counts": {
"recipients": 412,
"sent": 0,
"delivered": 0,
"read": 0,
"replied": 0,
"failed": 0,
"skipped": 0
},
"scheduledAt": null,
"startedAt": "2026-10-06T10:00:02.311Z",
"finishedAt": null,
"createdAt": "2026-10-06T10:00:01.874Z",
"lastError": null,
"message": "Sending started. Messages go out in the background."
}Authorizations
Your API key (ta_live_…) from Settings → API keys, sent as Authorization: Bearer <key>. Keep it on a server: it can message every customer you have. A key is full, build or read-only; a request its level does not allow is refused with 403.
Headers
Developers only: the customer workspace this request acts in — its org_… id from POST /customers. Leave it out to act in your own workspace.
"org_3fKq9TzQ1mN8vB2xR7cLpA"
Body
- WhatsApp broadcast
- Calling campaign
A WhatsApp broadcast or a calling campaign, told apart by kind.
whatsapp for a broadcast.
"whatsapp"What the console lists it as. Runs of spaces are collapsed.
2 - 120"Diwali sale — VIP customers"
The name of an approved template in this workspace (List Templates).
"diwali_offer_v2"
The template's language. Needed only when the name exists in more than one language.
"en"
The agent that answers replies to this broadcast (ag_…). Leave it out and the agent on your connected WhatsApp number answers.
"ag_5c4a5f93-2b1e-4c0a-9d3f-7e8a1b2c3d4e"
What fills each blank. Every {{n}} in the body, and the blank in a link button's address, must be filled.
20Show child attributes
Show child attributes
When the offer's countdown ends. Required, and in the future, when the template counts down to an offer.
Which contacts the campaign draws from. whatsapp_consented: people who messaged you on WhatsApp and agreed to hear from you — the safest list, and a broadcast's default. consented: everyone who agreed to marketing, on any channel — a calling campaign's default. imported: contacts that came from your own imports; they may never have contacted you. everyone: every contact with a number. People who asked not to be contacted are left out whichever you choose.
whatsapp_consented, consented, imported, everyone Reach only contacts carrying at least one of these tags. Narrows the audience, never widens it. Tags are lower-cased, spaces become hyphens, and at most 20 are kept. To aim at your own list, add the contacts with Create Contact and a tag first.
["vip"]
Reach the people from an earlier campaign who answered it a certain way. Both fields, or leave the whole object out.
Show child attributes
Show child attributes
Start the campaign by itself at this time, in the future. Do not send it together with launch.
true also starts it now, with Launch Campaign's checks. A refused launch keeps the draft and message says why.
Response
Created. The campaign as Get Campaign shows it, plus message: how many people it will reach, that it started, or why a requested launch did not happen (the campaign is then a draft you can launch later).
The campaign, plus a sentence about what just happened.
The campaign's id.
"cmp_9a3b7c21-4d5e-4f60-8a1b-2c3d4e5f6a7b"
A calling campaign: the agent that makes the calls. A broadcast: the agent that answers replies to it.
"ag_5c4a5f93-2b1e-4c0a-9d3f-7e8a1b2c3d4e"
The campaign's name, as the console lists it.
whatsapp for a broadcast, voice for a calling campaign.
whatsapp, voice draft until launched (a scheduled campaign stays a draft until scheduledAt), sending while it runs, paused when stopped for now, then sent, failed (it could not run; see lastError) or cancelled.
draft, sending, paused, sent, failed, cancelled A broadcast's template name. null on a calling campaign.
A calling campaign's opening line: the sentence the agent opens each call with, copied from the agent when the campaign was created (editing the agent later does not change it). null on a broadcast.
How far the campaign has got, person by person.
Show child attributes
Show child attributes
When a scheduled campaign starts itself. null when it is started by hand.
When it started sending. null while a draft.
When it finished or was cancelled. null while it can still send.
When it was created.
Why the campaign last stopped or failed, when it did. Cleared when it is launched or resumed.
What happened, in a sentence you can show a person: how many people it will reach, that sending or calling started (and, when the balance covers only part of it, how far it goes), or — on create with launch: true — why the launch was refused and the campaign kept as a draft.
"Created. 412 people will receive it."