DoneTalk Developers Sign in

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): an approved template 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: people carries each person's phone, name and values (up to 20,000; each phone becomes a contact, created or updated, which also needs the contacts:write scope). You may also use existing contacts (contactIds), a segment ({"kind": "segment", "segmentId": "…"}) or a filter.
  • Variables: each template {{n}} comes from name, first_name, phone, a contact field (field.<key>), a value from the list (value.<key>) or fixed text. fallback is used when the value is missing.
  • When: without startAt it 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.

This page as Markdown