Campaigns, start to finish
A campaign sends a template approved by Meta, from one of your numbers, to a list of contacts, and routes the replies to whoever you choose (a team, a flow or an AI assistant). This guide runs a whole campaign through the API. Try it all first with the demo key.
1. What you need first
curl https://api.donetalk.com/v1/c/{company}/channels -H "Authorization: Bearer $DONETALK_KEY"
curl https://api.donetalk.com/v1/c/{company}/templates -H "Authorization: Bearer $DONETALK_KEY"
curl https://api.donetalk.com/v1/c/{company}/teams -H "Authorization: Bearer $DONETALK_KEY"- Number (
channelId): where the message comes from. - Template (
templateId): anapprovedtemplate of the number's account. Its variables show in the text as{{1}},{{2}}. - Team (
reply.id): who gets the replies.
2. Create the draft, with the list
curl -X POST https://api.donetalk.com/v1/c/{company}/campaigns \
-H "Authorization: Bearer $DONETALK_KEY" \
-H "Idempotency-Key: reactivation-2026-10" \
-H "Content-Type: application/json" \
-d '{
"name": "Reactivation · October",
"channelId": "…",
"templateId": "…",
"variables": [
{ "slot": "body.1", "source": "first_name", "fallback": "there" },
{ "slot": "body.2", "source": "value.coupon", "format": "upper" }
],
"audience": {
"kind": "contacts",
"people": [
{ "phone": "+5548999990001", "name": "Ana Souza", "values": { "coupon": "ana10" } },
{ "phone": "+5548999990002", "name": "Bruno Lima", "values": { "coupon": "bru15" } }
]
},
"reply": { "kind": "team", "id": "…" },
"startAt": "2026-10-24T12:00:00.000Z"
}'- Audience:
peoplecarries each person's phone, name and values (up to 20,000; each phone becomes a contact, created or updated, which also needs thecontacts:writescope). You may also use existing contacts (contactIds), a segment ({"kind": "segment", "segmentId": "…"}) or a filter. - Variables: each template
{{n}}comes fromname,first_name,phone, a contact field (field.<key>), a value from the list (value.<key>) or fixed text.fallbackis used when the value is missing. - When: without
startAtit starts when sent; with it, it is scheduled. By default it only sends Monday to Friday, 9:00 to 18:00, in the company's time zone (sendWindow).
3. Check before sending
curl -X POST https://api.donetalk.com/v1/c/{company}/campaigns/{id}/message-preview -H "Authorization: Bearer $DONETALK_KEY"
curl -X POST https://api.donetalk.com/v1/c/{company}/campaigns/{id}/review -H "Authorization: Bearer $DONETALK_KEY"The preview shows the text for five people and the empty variables. The review gives the estimated cost (up to), the number's daily limit, the budgets and whether approval will be needed. POST /campaigns/{id}/test sends the message to your phone.
4. Send
curl -X POST https://api.donetalk.com/v1/c/{company}/campaigns/{id}/submit -H "Authorization: Bearer $DONETALK_KEY"The answer says running (started), scheduled or pending_approval (the company's rule asks for approval, and someone approves it in the app). A key marked "Start campaigns without waiting for approval" does not wait. Whoever got marketing in the last 7 days, opted out or is blocked is left out; the audience preview says how many and why.
5. Follow it
Through webhooks, with no polling: campaign.started, campaign.recipient.sent, .delivered, .read, .replied, .failed, .opted_out and campaign.completed, with the totals. When the conversation the campaign opened is closed, conversation.closed arrives with the outcome and the campaignId.
Or by asking:
curl "https://api.donetalk.com/v1/c/{company}/campaigns/{id}" -H "Authorization: Bearer $DONETALK_KEY"
curl "https://api.donetalk.com/v1/c/{company}/campaigns/{id}/recipients?status=replied" -H "Authorization: Bearer $DONETALK_KEY"6. Pause, resume, cancel, retry
POST /campaigns/{id}/pause, /resume, /cancel. Failures worth retrying go back to the queue with POST /campaigns/{id}/retry (all, or only some contacts or error codes). A campaign also pauses by itself if Meta pauses the template, the number's quality drops, many people opt out or the budget runs out.