# DoneTalk API (English): Developers # DoneTalk API The API lets your system (ERP, CRM, website, automation) do in DoneTalk what people do on screen: create and send WhatsApp campaigns, add and update contacts, send messages, follow and close conversations, book appointments and receive webhooks for everything that happens. - **Address:** `https://api.donetalk.com/v1/c/{company}`, where `{company}` is your company's identifier in DoneTalk. - **Format:** JSON, dates in ISO 8601 (UTC), ids as UUIDs, money in micros (1,000,000 = 1 unit of currency). - **Full specification:** [OpenAPI](https://api.donetalk.com/v1/openapi.json?lang=en), to generate code or import into Postman. ## Get started 1. An administrator creates the key in **Settings › API** in DoneTalk and ticks what it may do. To start with no risk, choose **Test**: it simulates everything and sends nothing. 2. Check the key: ```bash curl https://api.donetalk.com/v1/c/{company}/me \ -H "Authorization: Bearer $DONETALK_KEY" ``` 3. Create a campaign with the contact list in the call itself, review its cost and send it. The [Campaigns, start to finish](/en/developers/campaigns/) guide shows each step. 4. Register an address of your system to receive [webhooks](/en/developers/webhooks/): delivered, read, replied, conversation closed with its outcome. ## Try it now, no account needed The demo company `demo` accepts the public test key below. Everything is simulated: answers have the real shape, with example data, and nothing is stored or sent. ```bash export DONETALK_KEY=dt_test_demo_zRZ6twAhj4u9dZT0hEBrNFzr curl -X POST https://api.donetalk.com/v1/c/demo/campaigns \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "First campaign", "audience": {"kind": "contacts", "people": [{"phone": "+5548999990001", "name": "Ana"}]}}' ``` ## What the API covers | Area | What you can do | | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | [Campaigns](/en/developers/reference/campaigns/) | create, review cost and rules, test, send or schedule, pause, resume, cancel, retry failures, see each recipient and the results | | [Segments](/en/developers/reference/segments/) | groups of contacts by conditions, count and search | | [Contacts](/en/developers/reference/contacts/) | create, update or import up to 1,000 at a time, tags, consent, context cards, erase at the person's request | | [Fields](/en/developers/reference/contact-fields/) | read and create contact fields | | [Templates](/en/developers/reference/templates/) | the templates Meta approved, with variables and buttons | | [Messages](/en/developers/reference/messages/) and [conversations](/en/developers/reference/conversations/) | send a template to a phone, text and files within the 24-hour window, close with an outcome | | [Numbers, teams and tags](/en/developers/reference/numbers-teams-and-tags/) | to pick the number that sends and the team that gets the replies | | [Scheduling](/en/developers/reference/scheduling/) | free times, book, move and cancel | | [Webhooks](/en/developers/reference/webhooks/) | create and follow the addresses that receive the events | ## Before going live - Use one key per system, with only the scopes it needs ([Keys and scopes](/en/developers/keys-and-scopes/)). - Send `Idempotency-Key` on creations and sends and respect the limit of 600 calls per minute ([Errors and limits](/en/developers/errors-and-limits/)). - Check every webhook's signature ([Webhooks](/en/developers/webhooks/)). --- # Keys and scopes ## Create a key In **Settings › API**, an administrator creates the key and chooses: - **Name:** the system that will use it (e.g. "Company ERP"). - **Environment:** **Production** acts for real; **Test** simulates everything ([Test mode](/en/developers/test-mode/)). - **What the key may do:** the scopes below. A call outside the key's scopes returns `403 insufficient_scope`, with the scopes that work in `details.required`. - **Start campaigns without waiting for approval:** without it, this key's campaigns follow the company's approval rule (the budget always applies). - **Valid until** and **only from these IP addresses**, optional. The whole key is shown only once, when created. Keep it like a password. Production keys start with `dt_live_`, test keys with `dt_test_`. ## Authenticate Send the key on every call: ```http Authorization: Bearer dt_live_... ``` `Authorization: ApiKey dt_live_...` works too. The company comes from the address (`/v1/c/{company}/...`) and must be the key's. ## Scopes | Group | Scope | What it allows | |---|---|---| | Campaigns | `campaigns:read` | See campaigns and results | | Campaigns | `campaigns:write` | Create and edit drafts | | Campaigns | `campaigns:send` | Start, pause and cancel | | Contacts | `contacts:read` | See contacts and their field values | | Contacts | `contacts:write` | Create and update contacts | | Contacts | `fields:read` | See the list of fields | | Contacts | `fields:write` | Create fields | | Contacts | `contacts:delete` | Erase contacts' data (LGPD) | | Templates | `templates:read` | See templates | | Templates | `templates:write` | Create templates and submit them to Meta | | Messages and conversations | `messages:send_template` | Send templates | | Messages and conversations | `messages:send_text` | Send text and files (24-hour window) | | Messages and conversations | `conversations:read` | See conversations and messages | | Messages and conversations | `conversations:write` | Close and reopen conversations | | Flows and scheduling | `flows:start` | Start flows | | Flows and scheduling | `appointments:read` | See appointments | | Flows and scheduling | `appointments:write` | Book, move and cancel appointments | | Other | `webhooks:manage` | Manage webhooks | | Other | `reports:read` | See reports and costs | | Other | `workspace:read` | See numbers, teams and tags | | Other | `integrations:outboundpro` | Connect OutboundPro Conversas | ## Older keys Keys made before scopes (`dsf_live_...`) keep working with the integration access they had. Choosing scopes for one of them limits it to those. ## Security - One key per system, with the fewest scopes. - If a key leaks, revoke it in Settings › API: it stops at once. - Creating, editing and revoking are recorded in the Audit log, with who did it. --- # Test mode A **Test** key (`dt_test_...`) answers like a production one, with example data. Nothing is stored, no message is sent and nothing is charged. - Each call is checked by the same rules as production: a wrong field gives the same `422 validation_failed`. - The answer has the real shape, with `"test": true` and the `DoneTalk-Test: true` header. - `GET /me` answers for real, to check the key. - Scopes, the rate limit and `Idempotency-Key` work as in production. ## Simulate an error Send the `DoneTalk-Simulate` header with an error code to get exactly the answer production would give: ```bash curl -X POST https://api.donetalk.com/v1/c/demo/campaigns \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "DoneTalk-Simulate: template_not_approved" \ -H "Content-Type: application/json" \ -d '{"name": "Test"}' ``` Accepted codes: `unauthorized`, `api_key_invalid`, `api_key_expired`, `api_key_address_not_allowed`, `insufficient_scope`, `not_available_to_api_keys`, `not_available_to_test_keys`, `permission_denied`, `integration_template_only`, `not_found`, `campaign_not_found`, `contact_not_found`, `validation_failed`, `campaign_not_editable`, `template_not_approved`, `campaign_audience_empty`, `contact_exists`, `window_closed`, `idempotency_key_reused`, `idempotency_in_progress`, `rate_limited`, `internal_error`. ## Magic phones In any phone field, these numbers cause a known behaviour: | Phone | What happens | |---|---| | `+550000000400` | `validation_failed` (422) | | `+550000000404` | `contact_not_found` (404) | | `+550000000409` | `contact_exists` (409) | | `+550000000429` | `rate_limited` (429) | | `+550000000500` | `internal_error` (500) | | `+550000000131` | message.failed / campaign.recipient.failed: `131026` | | `+550000000470` | message.failed / campaign.recipient.failed: `131047` | ## Test webhooks A send with a test key (a message, a campaign) delivers to the company's webhooks the sequence production would, a few seconds apart: sent, delivered, read and, for a campaign, replied and completed. With a failing magic phone, the failure arrives. The envelope carries `"test": true`. ## Demo company To try without an account, use the `demo` company with the public key: ```bash export DONETALK_KEY=dt_test_demo_zRZ6twAhj4u9dZT0hEBrNFzr curl https://api.donetalk.com/v1/c/demo/me -H "Authorization: Bearer $DONETALK_KEY" ``` It only simulates: nothing real can be done with it. --- # Errors, limits and good practice ## Error format Every error comes back like this: ```json { "error": "validation_failed", "message": "A field is wrong. `details` says which and why.", "details": { "name": ["Required"] }, "request_id": "01a3c0de-7a11-7b22-8c33-9d44e55f6677" } ``` - `error` is the stable code: your system decides what to do from it. - `message` explains, in the language of the `Accept-Language` header (Portuguese, English or Spanish; English by default). - `request_id` identifies the call: quote it to support. You may send your own in `X-Request-Id`. | HTTP | Code | What to do | |---|---|---| | 401 | `unauthorized` | The key is missing. Send Authorization: Bearer . | | 401 | `api_key_invalid` | Invalid or revoked key. Check it or create another in Settings › API. | | 401 | `api_key_expired` | The key has expired. Create another in Settings › API. | | 403 | `api_key_address_not_allowed` | This key does not accept calls from this IP address. Add the address to the key. | | 403 | `insufficient_scope` | The key lacks this call's scope. `details.required` lists the ones that work. | | 403 | `not_available_to_api_keys` | This call is not part of the public API. | | 403 | `not_available_to_test_keys` | This call has no test mode: use a production key. | | 403 | `permission_denied` | The key may not do this. | | 403 | `integration_template_only` | Text and files need the messages:send_text scope; without it, approved templates only. | | 404 | `not_found` | Not found. | | 404 | `campaign_not_found` | Campaign not found in this company. | | 404 | `contact_not_found` | Contact not found in this company. | | 422 | `validation_failed` | A field is wrong. `details` says which and why. | | 409 | `campaign_not_editable` | The campaign is no longer a draft; only a draft can be edited or started. | | 409 | `template_not_approved` | Meta has not approved the template yet. Wait for approval or pick another. | | 409 | `campaign_audience_empty` | No one in the audience can receive the campaign. See why in the audience preview. | | 409 | `contact_exists` | A contact with this phone already exists. `details.contactId` says which. | | 409 | `window_closed` | The 24-hour window is closed: only an approved template can start the conversation. | | 409 | `idempotency_key_reused` | This Idempotency-Key was already used with another request. Use a new key per request. | | 409 | `idempotency_in_progress` | The first request with this Idempotency-Key is still running. Retry in a moment. | | 429 | `rate_limited` | Limit of 600 calls per minute. Wait the seconds in Retry-After. | | 500 | `internal_error` | Error on our side. We were alerted; retry, and quote the request_id if it persists. | ## Rate limit Each key may make 600 calls per minute. Every answer says where you are: ```http RateLimit-Limit: 600 RateLimit-Remaining: 587 RateLimit-Reset: 41 ``` Past the limit, the answer is `429 rate_limited` with `Retry-After` (seconds). Wait that long and retry. A campaign's sending does not count: DoneTalk sends at the pace Meta allows for the number. ## Retry without duplicating Send `Idempotency-Key` (up to 200 letters, digits, `_ . : -`) on creations and sends. If the network fails and you send the same call again with the same key within 24 hours, you get the first answer back, with `Idempotent-Replayed: true`, and nothing is done twice. The same key with another request returns `409 idempotency_key_reused`. ```http Idempotency-Key: order-88213-campaign ``` ## Paging - **Campaigns:** `limit` and `cursor`; the answer carries `nextCursor` (null on the last page). - **Contacts:** `limit` and `before` (the page's last `lastActivityAt`). - **A campaign's recipients:** `limit` and `offset`, or `format=csv` for everything. ## Versions v1 only gains things: more fields and calls, never a change to what exists. Ignore fields you do not know. If something ever must change, v2 is born and v1 keeps working for 12 months, with notice. --- # Webhooks DoneTalk tells your system what happens, with a signed POST to an address of yours: a campaign started or finished, a message was delivered or read, a contact replied, a conversation was closed with its outcome. ## Register an address In **Settings › Integrations › Outgoing webhooks**, or through the API with the `webhooks:manage` scope: ```bash curl -X POST https://api.donetalk.com/v1/c/{company}/webhooks \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://your-system.com/donetalk/events", "events": ["campaign.completed", "campaign.recipient.replied", "conversation.closed"]}' ``` The answer carries the `secret` only once: keep it to check the signatures. ## What arrives ```http POST /donetalk/events HTTP/1.1 Content-Type: application/json User-Agent: DoneTalk-Webhooks/1.0 DoneTalk-Signature: t=1792756800,v1=5f0c… X-DoneTalk-Event: conversation.closed X-DoneTalk-Delivery: 01a3e4f5-a6b7-7c8d-9e0f-1a2b3c4d5e6f ``` ```json { "id": "01a3e4f5-…", "type": "conversation.closed", "occurredAt": "2026-10-24T12:20:41.000Z", "customerId": "01a0c0de-…", "data": { "...": "the event's content" } } ``` Events from a test key carry `"test": true`. ## Check the signature `DoneTalk-Signature` carries `t` (when it was sent, in seconds) and `v1`, the HMAC SHA-256 of `"."` with your `secret`. Refuse events with a `t` older than 5 minutes: then no one can replay an old event. ```js import crypto from "node:crypto"; function validEvent(rawBody, header, secret) { const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("="))); if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex"); return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)); } ``` ```python import hmac, hashlib, time def valid_event(raw_body: bytes, header: str, secret: str) -> bool: parts = dict(p.split("=", 1) for p in header.split(",")) if abs(time.time() - int(parts["t"])) > 300: return False expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, parts["v1"]) ``` The older `x-donetalk-signature` header (`sha256=` + HMAC of the body, without the time) goes along until 2027-01-21. ## Answering and retries Answer `2xx` within 10 seconds; do heavy work afterwards. Without a `2xx`, DoneTalk retries after 1 min, 5 min, 30 min, 2 h and 12 h. After 20 failures in a row the address shows as "Failing" (it keeps receiving). The same event may arrive more than once: use its `id` to process it once. Arrival order is not guaranteed: use `occurredAt`. ## Events | Event | When it arrives | |---|---| | `conversation.opened` | A conversation started (the contact wrote or clicked an ad). | | `conversation.closed` | A conversation was closed, with the outcome, who closed it (person, AI assistant or automatic rule), the closing note, the contact, team, tags, how long it took and the campaign it came from. | | `conversation.assigned` | A conversation was assigned to a person. | | `conversation.transferred` | A conversation moved to another team or person. | | `conversation.reopened` | A closed conversation was reopened. | | `contact.updated` | A contact's data changed. | | `contact.consent_changed` | The contact opted in or out of a purpose (marketing, for example). | | `message.received` | The contact sent a message. | | `message.sent` | A message of ours left for WhatsApp. | | `message.delivered` | A message reached the contact's phone. | | `message.read` | The contact read a message. | | `message.failed` | A message could not be delivered; `error.code` is Meta's code. | | `flow.event` | A flow reached a “Record event” block, with the name and data the block sets. | | `appointment.booked` | An appointment was booked. | | `appointment.rescheduled` | An appointment was moved. | | `appointment.cancelled` | An appointment was cancelled. | | `appointment.confirmed` | The contact confirmed the appointment. | | `appointment.attended` | The contact attended. | | `appointment.no_show` | The contact did not show up. | | `campaign.started` | The campaign started sending; its list was frozen. | | `campaign.paused` | The campaign paused: by a person, the API or a rule (template paused, low quality, many opt-outs, budget). | | `campaign.resumed` | The campaign resumed sending. | | `campaign.completed` | The campaign finished sending, with its totals. | | `campaign.cancelled` | The campaign was cancelled. | | `campaign.recipient.sent` | The campaign's message left for a contact. | | `campaign.recipient.delivered` | The campaign's message reached the contact. | | `campaign.recipient.read` | The contact read the campaign's message. | | `campaign.recipient.replied` | The contact answered the campaign (the first answer), by text or tapping a button. | | `campaign.recipient.failed` | The campaign's message failed for a contact; `errorCode` is Meta's code. | | `campaign.recipient.opted_out` | The contact asked for no more campaigns (the “Stop promotions” button or a refusal at Meta). | ## Example of each event ### conversation.opened A conversation started (the contact wrote or clicked an ad). ```json { "id": "01a3e4f5-…", "type": "conversation.opened", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "channelId": "01a0cccc-0000-7000-8000-000000000002", "handler": "flow", "source": "inbound" } } ``` ### conversation.closed A conversation was closed, with the outcome, who closed it (person, AI assistant or automatic rule), the closing note, the contact, team, tags, how long it took and the campaign it came from. ```json { "id": "01a3e4f5-…", "type": "conversation.closed", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "channelId": "01a0cccc-0000-7000-8000-000000000002", "teamId": "01a0cb22-89c0-7e02-b96b-3f4731dde701", "assigneeId": "01a0cac1-4a6f-7646-b5f8-c1d19090016a", "outcome": "converted", "isSuccess": true, "closedBy": { "kind": "member", "id": "01a0cac1-4a6f-7646-b5f8-c1d19090016a", "name": "Tayomara Batista" }, "actorKind": "member", "openedAt": "2026-10-24T12:00:04.000Z", "firstResponseAt": "2026-10-24T12:05:10.000Z", "messagesIn": 6, "messagesOut": 7, "outcomeLabel": { "pt-BR": "Venda", "en": "Sale", "es": "Venta" }, "outcomeCategory": "success", "note": "Fechou o plano anual com o cupom ANA10.", "contact": { "name": "Ana Souza", "phone": "5548999990001" }, "team": { "id": "01a0cb22-89c0-7e02-b96b-3f4731dde701", "name": "Vendas" }, "tags": [ { "id": "01a12150-3610-7c00-b680-188c2b4c7313", "name": { "pt-BR": "VIP", "en": "VIP", "es": "VIP" } } ], "durationSeconds": 1237, "campaignId": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b" } } ``` ### conversation.assigned A conversation was assigned to a person. ```json { "id": "01a3e4f5-…", "type": "conversation.assigned", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "assigneeId": "01a0cac1-4a6f-7646-b5f8-c1d19090016a", "teamId": "01a0cb22-89c0-7e02-b96b-3f4731dde701", "reason": "distribution", "previousAssigneeId": null, "actorKind": "system" } } ``` ### conversation.transferred A conversation moved to another team or person. ```json { "id": "01a3e4f5-…", "type": "conversation.transferred", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "teamId": "01a0cb22-89c0-7e02-b96b-3f4731dde701", "assigneeId": null, "previousTeamId": null, "previousAssigneeId": null, "previousHandler": "flow", "actorKind": "flow", "reason": "Gestor de conta: Tayomara Batista" } } ``` ### conversation.reopened A closed conversation was reopened. ```json { "id": "01a3e4f5-…", "type": "conversation.reopened", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677" } } ``` ### contact.updated A contact's data changed. ```json { "id": "01a3e4f5-…", "type": "contact.updated", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "changes": [ "fields", "displayName" ] } } ``` ### contact.consent_changed The contact opted in or out of a purpose (marketing, for example). ```json { "id": "01a3e4f5-…", "type": "contact.consent_changed", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "channel": "whatsapp", "purpose": "marketing", "status": "opted_out", "source": "keyword", "occurredAt": "2026-10-24T12:06:00.000Z" } } ``` ### message.received The contact sent a message. ```json { "id": "01a3e4f5-…", "type": "message.received", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "messageId": "01a3c0de-8b22-7c33-9d44-0e55f6677889", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "channelId": "01a0cccc-0000-7000-8000-000000000002", "type": "text" } } ``` ### message.sent A message of ours left for WhatsApp. ```json { "id": "01a3e4f5-…", "type": "message.sent", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "messageId": "01a3c0de-8b22-7c33-9d44-0e55f6677889", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "campaignId": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "providerMessageId": "wamid.HBgMNTU0ODk5OTk5MDAwMRUCABEYEjE=" } } ``` ### message.delivered A message reached the contact's phone. ```json { "id": "01a3e4f5-…", "type": "message.delivered", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "messageId": "01a3c0de-8b22-7c33-9d44-0e55f6677889", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "campaignId": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "status": "delivered", "occurredAt": "2026-10-24T12:00:06.000Z" } } ``` ### message.read The contact read a message. ```json { "id": "01a3e4f5-…", "type": "message.read", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "messageId": "01a3c0de-8b22-7c33-9d44-0e55f6677889", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "campaignId": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "status": "read", "occurredAt": "2026-10-24T12:03:51.000Z" } } ``` ### message.failed A message could not be delivered; `error.code` is Meta's code. ```json { "id": "01a3e4f5-…", "type": "message.failed", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "messageId": "01a3c0de-8b22-7c33-9d44-0e55f6677889", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "campaignId": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "status": "failed", "occurredAt": "2026-10-24T12:00:06.000Z", "error": { "code": "131026", "detail": "Message undeliverable" } } } ``` ### flow.event A flow reached a “Record event” block, with the name and data the block sets. ```json { "id": "01a3e4f5-…", "type": "flow.event", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "name": "lead.qualified", "data": { "plano": "anual", "origem": "site" } } } ``` ### appointment.booked An appointment was booked. ```json { "id": "01a3e4f5-…", "type": "appointment.booked", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "id": "01a2f00d-1234-7abc-8def-0123456789ab", "agendaId": "01a1228c-c584-73f7-b7e7-bfaa0d02fed0", "agendaName": "Visitas aos imóveis", "typeId": "01a1228c-c58f-796a-b3aa-54a953711c71", "typeName": "Visita ao imóvel", "typeColor": "#1daa61", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "contactName": "Ana Souza", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "channelId": "01a0cccc-0000-7000-8000-000000000002", "memberId": "01a0cac1-4a6f-7646-b5f8-c1d19090016a", "memberName": "Tayomara Batista", "holderIds": [ "01a0cac1-4a6f-7646-b5f8-c1d19090016a" ], "status": "booked", "startsAt": "2026-10-27T13:00:00.000Z", "endsAt": "2026-10-27T14:00:00.000Z", "timezone": "America/Sao_Paulo", "location": "Rua Bahia, 120 – Higienópolis, São Paulo", "locationKind": "address", "onlineUrl": null, "answers": { "imovel": "AP-2231" }, "notes": null, "source": "api", "createdByKind": "api_key", "createdById": null, "confirmedAt": null, "cancelledAt": null, "cancelledByKind": null, "cancelReason": null, "rescheduleCount": 0, "previousStartsAt": null, "publicToken": "r3WqZ8", "createdAt": "2026-10-23T13:02:11.000Z" } } ``` ### appointment.rescheduled An appointment was moved. ```json { "id": "01a3e4f5-…", "type": "appointment.rescheduled", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "id": "01a2f00d-1234-7abc-8def-0123456789ab", "agendaId": "01a1228c-c584-73f7-b7e7-bfaa0d02fed0", "agendaName": "Visitas aos imóveis", "typeId": "01a1228c-c58f-796a-b3aa-54a953711c71", "typeName": "Visita ao imóvel", "typeColor": "#1daa61", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "contactName": "Ana Souza", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "channelId": "01a0cccc-0000-7000-8000-000000000002", "memberId": "01a0cac1-4a6f-7646-b5f8-c1d19090016a", "memberName": "Tayomara Batista", "holderIds": [ "01a0cac1-4a6f-7646-b5f8-c1d19090016a" ], "status": "booked", "startsAt": "2026-10-27T13:00:00.000Z", "endsAt": "2026-10-27T14:00:00.000Z", "timezone": "America/Sao_Paulo", "location": "Rua Bahia, 120 – Higienópolis, São Paulo", "locationKind": "address", "onlineUrl": null, "answers": { "imovel": "AP-2231" }, "notes": null, "source": "api", "createdByKind": "api_key", "createdById": null, "confirmedAt": null, "cancelledAt": null, "cancelledByKind": null, "cancelReason": null, "rescheduleCount": 1, "previousStartsAt": "2026-10-27T12:00:00.000Z", "publicToken": "r3WqZ8", "createdAt": "2026-10-23T13:02:11.000Z" } } ``` ### appointment.cancelled An appointment was cancelled. ```json { "id": "01a3e4f5-…", "type": "appointment.cancelled", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "id": "01a2f00d-1234-7abc-8def-0123456789ab", "agendaId": "01a1228c-c584-73f7-b7e7-bfaa0d02fed0", "agendaName": "Visitas aos imóveis", "typeId": "01a1228c-c58f-796a-b3aa-54a953711c71", "typeName": "Visita ao imóvel", "typeColor": "#1daa61", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "contactName": "Ana Souza", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "channelId": "01a0cccc-0000-7000-8000-000000000002", "memberId": "01a0cac1-4a6f-7646-b5f8-c1d19090016a", "memberName": "Tayomara Batista", "holderIds": [ "01a0cac1-4a6f-7646-b5f8-c1d19090016a" ], "status": "cancelled", "startsAt": "2026-10-27T13:00:00.000Z", "endsAt": "2026-10-27T14:00:00.000Z", "timezone": "America/Sao_Paulo", "location": "Rua Bahia, 120 – Higienópolis, São Paulo", "locationKind": "address", "onlineUrl": null, "answers": { "imovel": "AP-2231" }, "notes": null, "source": "api", "createdByKind": "api_key", "createdById": null, "confirmedAt": null, "cancelledAt": "2026-10-26T18:00:00.000Z", "cancelledByKind": "contact", "cancelReason": null, "rescheduleCount": 0, "previousStartsAt": null, "publicToken": "r3WqZ8", "createdAt": "2026-10-23T13:02:11.000Z" } } ``` ### appointment.confirmed The contact confirmed the appointment. ```json { "id": "01a3e4f5-…", "type": "appointment.confirmed", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "id": "01a2f00d-1234-7abc-8def-0123456789ab", "agendaId": "01a1228c-c584-73f7-b7e7-bfaa0d02fed0", "agendaName": "Visitas aos imóveis", "typeId": "01a1228c-c58f-796a-b3aa-54a953711c71", "typeName": "Visita ao imóvel", "typeColor": "#1daa61", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "contactName": "Ana Souza", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "channelId": "01a0cccc-0000-7000-8000-000000000002", "memberId": "01a0cac1-4a6f-7646-b5f8-c1d19090016a", "memberName": "Tayomara Batista", "holderIds": [ "01a0cac1-4a6f-7646-b5f8-c1d19090016a" ], "status": "confirmed", "startsAt": "2026-10-27T13:00:00.000Z", "endsAt": "2026-10-27T14:00:00.000Z", "timezone": "America/Sao_Paulo", "location": "Rua Bahia, 120 – Higienópolis, São Paulo", "locationKind": "address", "onlineUrl": null, "answers": { "imovel": "AP-2231" }, "notes": null, "source": "api", "createdByKind": "api_key", "createdById": null, "confirmedAt": "2026-10-26T14:00:00.000Z", "cancelledAt": null, "cancelledByKind": null, "cancelReason": null, "rescheduleCount": 0, "previousStartsAt": null, "publicToken": "r3WqZ8", "createdAt": "2026-10-23T13:02:11.000Z" } } ``` ### appointment.attended The contact attended. ```json { "id": "01a3e4f5-…", "type": "appointment.attended", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "id": "01a2f00d-1234-7abc-8def-0123456789ab", "agendaId": "01a1228c-c584-73f7-b7e7-bfaa0d02fed0", "agendaName": "Visitas aos imóveis", "typeId": "01a1228c-c58f-796a-b3aa-54a953711c71", "typeName": "Visita ao imóvel", "typeColor": "#1daa61", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "contactName": "Ana Souza", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "channelId": "01a0cccc-0000-7000-8000-000000000002", "memberId": "01a0cac1-4a6f-7646-b5f8-c1d19090016a", "memberName": "Tayomara Batista", "holderIds": [ "01a0cac1-4a6f-7646-b5f8-c1d19090016a" ], "status": "attended", "startsAt": "2026-10-27T13:00:00.000Z", "endsAt": "2026-10-27T14:00:00.000Z", "timezone": "America/Sao_Paulo", "location": "Rua Bahia, 120 – Higienópolis, São Paulo", "locationKind": "address", "onlineUrl": null, "answers": { "imovel": "AP-2231" }, "notes": null, "source": "api", "createdByKind": "api_key", "createdById": null, "confirmedAt": null, "cancelledAt": null, "cancelledByKind": null, "cancelReason": null, "rescheduleCount": 0, "previousStartsAt": null, "publicToken": "r3WqZ8", "createdAt": "2026-10-23T13:02:11.000Z" } } ``` ### appointment.no_show The contact did not show up. ```json { "id": "01a3e4f5-…", "type": "appointment.no_show", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "id": "01a2f00d-1234-7abc-8def-0123456789ab", "agendaId": "01a1228c-c584-73f7-b7e7-bfaa0d02fed0", "agendaName": "Visitas aos imóveis", "typeId": "01a1228c-c58f-796a-b3aa-54a953711c71", "typeName": "Visita ao imóvel", "typeColor": "#1daa61", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "contactName": "Ana Souza", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "channelId": "01a0cccc-0000-7000-8000-000000000002", "memberId": "01a0cac1-4a6f-7646-b5f8-c1d19090016a", "memberName": "Tayomara Batista", "holderIds": [ "01a0cac1-4a6f-7646-b5f8-c1d19090016a" ], "status": "no_show", "startsAt": "2026-10-27T13:00:00.000Z", "endsAt": "2026-10-27T14:00:00.000Z", "timezone": "America/Sao_Paulo", "location": "Rua Bahia, 120 – Higienópolis, São Paulo", "locationKind": "address", "onlineUrl": null, "answers": { "imovel": "AP-2231" }, "notes": null, "source": "api", "createdByKind": "api_key", "createdById": null, "confirmedAt": null, "cancelledAt": null, "cancelledByKind": null, "cancelReason": null, "rescheduleCount": 0, "previousStartsAt": null, "publicToken": "r3WqZ8", "createdAt": "2026-10-23T13:02:11.000Z" } } ``` ### campaign.started The campaign started sending; its list was frozen. ```json { "id": "01a3e4f5-…", "type": "campaign.started", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "campaignId": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "status": "running", "audience": 1240, "skipped": 24, "startedAt": "2026-10-24T12:00:00.000Z" } } ``` ### campaign.paused The campaign paused: by a person, the API or a rule (template paused, low quality, many opt-outs, budget). ```json { "id": "01a3e4f5-…", "type": "campaign.paused", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "campaignId": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "status": "paused", "reason": "optout_rate", "by": "system" } } ``` ### campaign.resumed The campaign resumed sending. ```json { "id": "01a3e4f5-…", "type": "campaign.resumed", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "campaignId": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "status": "running", "by": "api_key" } } ``` ### campaign.completed The campaign finished sending, with its totals. ```json { "id": "01a3e4f5-…", "type": "campaign.completed", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "campaignId": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "status": "completed", "totals": { "audience": 1216, "sent": 1216, "delivered": 1198, "read": 874, "replied": 141, "failed": 18, "skipped": 24, "optedOut": 6 } } } ``` ### campaign.cancelled The campaign was cancelled. ```json { "id": "01a3e4f5-…", "type": "campaign.cancelled", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "campaignId": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "status": "cancelled", "by": "member" } } ``` ### campaign.recipient.sent The campaign's message left for a contact. ```json { "id": "01a3e4f5-…", "type": "campaign.recipient.sent", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "campaignId": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "phone": "5548999990001", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "messageId": "01a3c0de-8b22-7c33-9d44-0e55f6677889", "at": "2026-10-24T12:00:04.000Z" } } ``` ### campaign.recipient.delivered The campaign's message reached the contact. ```json { "id": "01a3e4f5-…", "type": "campaign.recipient.delivered", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "campaignId": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "phone": "5548999990001", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "messageId": "01a3c0de-8b22-7c33-9d44-0e55f6677889", "at": "2026-10-24T12:00:06.000Z" } } ``` ### campaign.recipient.read The contact read the campaign's message. ```json { "id": "01a3e4f5-…", "type": "campaign.recipient.read", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "campaignId": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "phone": "5548999990001", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "messageId": "01a3c0de-8b22-7c33-9d44-0e55f6677889", "at": "2026-10-24T12:03:51.000Z" } } ``` ### campaign.recipient.replied The contact answered the campaign (the first answer), by text or tapping a button. ```json { "id": "01a3e4f5-…", "type": "campaign.recipient.replied", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "campaignId": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "buttonIndex": 0, "text": "Quero o cupom" } } ``` ### campaign.recipient.failed The campaign's message failed for a contact; `errorCode` is Meta's code. ```json { "id": "01a3e4f5-…", "type": "campaign.recipient.failed", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "campaignId": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "phone": "5548999990001", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "messageId": "01a3c0de-8b22-7c33-9d44-0e55f6677889", "errorCode": "131026" } } ``` ### campaign.recipient.opted_out The contact asked for no more campaigns (the “Stop promotions” button or a refusal at Meta). ```json { "id": "01a3e4f5-…", "type": "campaign.recipient.opted_out", "occurredAt": "2026-10-24T12:04:02.000Z", "customerId": "01a0c0de-…", "data": { "campaignId": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "via": "button" } } ``` --- # 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](/en/developers/test-mode/). ## 1. What you need first ```bash 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 ```bash 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.`), a value from the list (`value.`) 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 ```bash 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 ```bash 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](/en/developers/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: ```bash 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. --- # For AIs and agents This documentation is made to be read and tried by an AI, with no account and no risk. ## Where to read - [llms.txt](/en/developers/llms.txt): the index, with a link to each page as Markdown. - [llms-full.txt](/en/developers/llms-full.txt): the whole documentation in one file. - Every page also exists as Markdown: replace the address's final slash with `.md` (e.g. `/en/developers/campaigns.md`). - [OpenAPI](https://api.donetalk.com/v1/openapi.json?lang=en): every call, field, scope, error and example. `x-webhooks` holds the events; `x-errors`, the errors; `x-test-mode`, the test mode. ## Try it for real, with no risk The `demo` company accepts the public test key `dt_test_demo_zRZ6twAhj4u9dZT0hEBrNFzr`. It only simulates: it checks requests by the real rules and answers in the real shape, with example data, storing and sending nothing. ```bash export DONETALK_KEY=dt_test_demo_zRZ6twAhj4u9dZT0hEBrNFzr curl https://api.donetalk.com/v1/c/demo/me -H "Authorization: Bearer $DONETALK_KEY" ``` To see an error, ask for it with `DoneTalk-Simulate: ` ([Test mode](/en/developers/test-mode/)). ## A prompt ready to paste into an AI > Read https://donetalk.com/en/developers/llms-full.txt and, using the demo company with the public test key, create a campaign for two contacts with a coupon each, review it and send it. Then show how my system would know a contact replied. ## Rules for agents - Use a **Test** key while building; production only afterwards. - Send `Idempotency-Key` on every creation and send. - Respect `Retry-After` on a `429`. - Never message real numbers without the contact's consent: marketing needs an opt-in. --- # Account ## Who the key is `GET /v1/c/{slug}/me` · Scopes: any key The company, the key's name, whether it is live or test, its scopes and the rate limit. A good first call for an integration. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/me" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "memberId": "01a1b2c3-d4e5-7f60-8a9b-0c1d2e3f4a5b", "role": "integration", "permissions": [ "api.use", "campaigns.read", "campaigns.start", "campaigns.write", "contacts.read", "contacts.write" ], "company": { "slug": "captei", "name": "Captei" }, "key": { "name": "ERP Captei", "prefix": "dt_live_7Hq2mV0c", "environment": "live", "scopes": [ "campaigns:read", "campaigns:write", "campaigns:send", "contacts:write" ], "legacy": false, "skipApproval": false, "expiresAt": null }, "rateLimit": { "limit": 600, "windowSeconds": 60 } } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` --- # Campaigns ## List campaigns `GET /v1/c/{slug}/campaigns` · Scopes: `campaigns:read` Newest first, each with its numbers. Filter by `status`. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Query parameters | Field | Type | Required | Notes | |---|---|---|---| | `status` | string | no | up to 40 characters | | `limit` | integer | no | up to 200; at least 1; default `200` | | `cursor` | string | no | up to 200 characters | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/campaigns" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "campaigns": [ { "id": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "name": "Reativação · outubro", "status": "draft", "channel": { "id": "01a0cccc-0000-7000-8000-000000000002", "name": "Captei" }, "template": { "id": "01a0cad0-0000-7000-8000-000000000002", "name": "reativacao_cupom", "category": "marketing" }, "variables": [ { "slot": "body.1", "source": "first_name", "format": "none", "fallback": "tudo bem" }, { "slot": "body.2", "source": "value.cupom", "format": "upper", "fallback": "" } ], "buttons": [ { "index": 0, "action": "route" } ], "audience": { "kind": "contacts", "contactIds": [ "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "01a0d05a-3ee3-75bd-9db5-dc9856f079c9" ] }, "excludeOpen": true, "frequencyDays": 7, "startAt": "2026-10-24T12:00:00.000Z", "sendWindow": [ { "day": 1, "from": "09:00", "to": "18:00" } ], "reply": { "kind": "team", "id": "01a0cb22-89c0-7e02-b96b-3f4731dde701", "goal": null }, "successDays": 7, "stopRules": { "templatePaused": true, "qualityLow": true, "optoutRate": 0.03, "budget": true }, "budgetMicros": null, "estimate": { "micros": 77824000, "currency": "BRL" }, "audienceCount": 1240, "header": null, "approval": { "required": false, "requestedTo": null, "requestedAt": null, "approvedBy": null, "approvedAt": null, "rejectedReason": null }, "createdBy": null, "createdByKind": "api", "startedAt": null, "paused": null, "completedAt": null, "createdAt": "2026-10-23T13:02:11.000Z", "updatedAt": "2026-10-23T13:02:11.000Z", "stats": { "audience": 1240, "queued": 0, "sending": 0, "sent": 1216, "delivered": 1198, "read": 874, "failed": 18, "skipped": 24, "replied": 141, "optedOut": 6, "successes": 37, "costMicros": 77824000, "followMicros": 1260000, "aiUsdMicros": 0 } } ], "nextCursor": null } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Create a campaign `POST /v1/c/{slug}/campaigns` · Scopes: `campaigns:write` Creates a draft. Nothing is sent until `submit`. The audience is a segment, a filter or a list of contacts: by id (`contactIds`) or by phone (`people`), with each person's own values for the `value.` variables. Each phone becomes a contact, created or updated, which also needs the contacts:write scope. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `name` | string | yes | up to 120 characters | | `channelId` | string (uuid) | no | null | | `templateId` | string (uuid) | no | null | | `variables` | list of object | no | up to 40 | | `variables[].slot` | string | yes | | | `variables[].source` | one of `name`, `first_name`, `phone`, `fixed` · string | yes | | | `variables[].value` | string | no | up to 500 characters | | `variables[].format` | one of `none`, `title`, `upper`, `lower`, `first` | no | default `"none"` | | `variables[].fallback` | string | no | up to 200 characters; default `""` | | `buttons` | list of object | no | up to 10 | | `buttons[].index` | integer | yes | up to 9 | | `buttons[].action` | one of `route`, `optout`, `none` | yes | | | `audience` | object | no | null | | `audience.kind` | one of `segment` | yes | | | `audience.segmentId` | string (uuid) | yes | | | `audience.conditions` | list of object | yes | up to 20 | | `audience.conditions[].field` | string | yes | up to 80 characters | | `audience.conditions[].op` | one of `eq`, `neq`, `has`, `not_has`, `contains`, `empty`, `not_empty`, `gt`, `lt`, `none` | yes | | | `audience.conditions[].value` | string · number · boolean · one of `null` | no | | | `audience.contactIds` | list of string (uuid) | no | up to 20000; default `[]` | | `audience.people` | list of object | no | up to 20000 | | `audience.people[].phone` | string | yes | up to 40 characters | | `audience.people[].name` | string | no | up to 200 characters | | `audience.people[].fields` | object | no | | | `audience.people[].tags` | list of string | no | up to 20 | | `audience.people[].values` | object | no | | | `audience.values` | object | no | | | `excludeOpen` | boolean | no | | | `frequencyDays` | integer | no | up to 90; null | | `startAt` | string (date-time) | no | null | | `sendWindow` | list of object | no | up to 14 | | `sendWindow[].day` | integer | yes | up to 7; at least 1 | | `sendWindow[].from` | string | yes | | | `sendWindow[].to` | string | yes | | | `reply` | object | no | | | `reply.kind` | one of `team`, `flow`, `assistant` | no | default `"team"` | | `reply.id` | string (uuid) | no | default `null`; null | | `reply.goal` | string | no | up to 200 characters; default `null`; null | | `successDays` | integer | no | up to 60; at least 1 | | `stopRules` | object | no | | | `stopRules.templatePaused` | boolean | no | default `true` | | `stopRules.qualityLow` | boolean | no | default `true` | | `stopRules.optoutRate` | number | no | up to 0.5; at least 0.005; default `0.03`; null | | `stopRules.budget` | boolean | no | default `true` | | `budgetMicros` | integer | no | up to 1000000000000000; null | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/campaigns" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Reativação · outubro", "channelId": "01a0cccc-0000-7000-8000-000000000002", "templateId": "01a0cad0-0000-7000-8000-000000000002", "variables": [ { "slot": "body.1", "source": "first_name", "fallback": "tudo bem" }, { "slot": "body.2", "source": "value.cupom", "format": "upper" } ], "buttons": [ { "index": 0, "action": "route" } ], "audience": { "kind": "contacts", "people": [ { "phone": "+5548999990001", "name": "Ana Souza", "values": { "cupom": "ana10" } }, { "phone": "+5548999990002", "name": "Bruno Lima", "values": { "cupom": "bru15" } } ] }, "reply": { "kind": "team", "id": "01a0cb22-89c0-7e02-b96b-3f4731dde701" }, "startAt": "2026-10-24T12:00:00.000Z" }' ``` ### Response 201 ```json { "campaign": { "id": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "name": "Reativação · outubro", "status": "draft", "channel": { "id": "01a0cccc-0000-7000-8000-000000000002", "name": "Captei" }, "template": { "id": "01a0cad0-0000-7000-8000-000000000002", "name": "reativacao_cupom", "category": "marketing" }, "variables": [ { "slot": "body.1", "source": "first_name", "format": "none", "fallback": "tudo bem" }, { "slot": "body.2", "source": "value.cupom", "format": "upper", "fallback": "" } ], "buttons": [ { "index": 0, "action": "route" } ], "audience": { "kind": "contacts", "contactIds": [ "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "01a0d05a-3ee3-75bd-9db5-dc9856f079c9" ] }, "excludeOpen": true, "frequencyDays": 7, "startAt": "2026-10-24T12:00:00.000Z", "sendWindow": [ { "day": 1, "from": "09:00", "to": "18:00" } ], "reply": { "kind": "team", "id": "01a0cb22-89c0-7e02-b96b-3f4731dde701", "goal": null }, "successDays": 7, "stopRules": { "templatePaused": true, "qualityLow": true, "optoutRate": 0.03, "budget": true }, "budgetMicros": null, "estimate": { "micros": 77824000, "currency": "BRL" }, "audienceCount": 1240, "header": null, "approval": { "required": false, "requestedTo": null, "requestedAt": null, "approvedBy": null, "approvedAt": null, "rejectedReason": null }, "createdBy": null, "createdByKind": "api", "startedAt": null, "paused": null, "completedAt": null, "createdAt": "2026-10-23T13:02:11.000Z", "updatedAt": "2026-10-23T13:02:11.000Z", "stats": { "audience": 1240, "queued": 0, "sending": 0, "sent": 1216, "delivered": 1198, "read": 874, "failed": 18, "skipped": 24, "replied": 141, "optedOut": 6, "successes": 37, "costMicros": 77824000, "followMicros": 1260000, "aiUsdMicros": 0 } } } ``` ### Possible errors - `400` `template_other_account` - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Get a campaign `GET /v1/c/{slug}/campaigns/{id}` · Scopes: `campaigns:read` The campaign with its funnel: sent, delivered, read, replies, failures, successes and cost (in micros: 1,000,000 = 1 unit of currency). ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/campaigns/:id" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "campaign": { "id": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "name": "Reativação · outubro", "status": "running", "channel": { "id": "01a0cccc-0000-7000-8000-000000000002", "name": "Captei" }, "template": { "id": "01a0cad0-0000-7000-8000-000000000002", "name": "reativacao_cupom", "category": "marketing" }, "variables": [ { "slot": "body.1", "source": "first_name", "format": "none", "fallback": "tudo bem" }, { "slot": "body.2", "source": "value.cupom", "format": "upper", "fallback": "" } ], "buttons": [ { "index": 0, "action": "route" } ], "audience": { "kind": "contacts", "contactIds": [ "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "01a0d05a-3ee3-75bd-9db5-dc9856f079c9" ] }, "excludeOpen": true, "frequencyDays": 7, "startAt": "2026-10-24T12:00:00.000Z", "sendWindow": [ { "day": 1, "from": "09:00", "to": "18:00" } ], "reply": { "kind": "team", "id": "01a0cb22-89c0-7e02-b96b-3f4731dde701", "goal": null }, "successDays": 7, "stopRules": { "templatePaused": true, "qualityLow": true, "optoutRate": 0.03, "budget": true }, "budgetMicros": null, "estimate": { "micros": 77824000, "currency": "BRL" }, "audienceCount": 1240, "header": null, "approval": { "required": false, "requestedTo": null, "requestedAt": null, "approvedBy": null, "approvedAt": null, "rejectedReason": null }, "createdBy": null, "createdByKind": "api", "startedAt": "2026-10-24T12:00:00.000Z", "paused": null, "completedAt": null, "createdAt": "2026-10-23T13:02:11.000Z", "updatedAt": "2026-10-23T13:02:11.000Z", "stats": { "audience": 1240, "queued": 0, "sending": 0, "sent": 1216, "delivered": 1198, "read": 874, "failed": 18, "skipped": 24, "replied": 141, "optedOut": 6, "successes": 37, "costMicros": 77824000, "followMicros": 1260000, "aiUsdMicros": 0 } } } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `404` `campaign_not_found` - `422` `validation_failed` - `429` `rate_limited` ## Edit a draft `PATCH /v1/c/{slug}/campaigns/{id}` · Scopes: `campaigns:write` Only a draft changes. Send only what changes. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `name` | string | no | up to 120 characters | | `channelId` | string (uuid) | no | null | | `templateId` | string (uuid) | no | null | | `variables` | list of object | no | up to 40 | | `variables[].slot` | string | yes | | | `variables[].source` | one of `name`, `first_name`, `phone`, `fixed` · string | yes | | | `variables[].value` | string | no | up to 500 characters | | `variables[].format` | one of `none`, `title`, `upper`, `lower`, `first` | no | default `"none"` | | `variables[].fallback` | string | no | up to 200 characters; default `""` | | `buttons` | list of object | no | up to 10 | | `buttons[].index` | integer | yes | up to 9 | | `buttons[].action` | one of `route`, `optout`, `none` | yes | | | `audience` | object | no | null | | `audience.kind` | one of `segment` | yes | | | `audience.segmentId` | string (uuid) | yes | | | `audience.conditions` | list of object | yes | up to 20 | | `audience.conditions[].field` | string | yes | up to 80 characters | | `audience.conditions[].op` | one of `eq`, `neq`, `has`, `not_has`, `contains`, `empty`, `not_empty`, `gt`, `lt`, `none` | yes | | | `audience.conditions[].value` | string · number · boolean · one of `null` | no | | | `audience.contactIds` | list of string (uuid) | no | up to 20000; default `[]` | | `audience.people` | list of object | no | up to 20000 | | `audience.people[].phone` | string | yes | up to 40 characters | | `audience.people[].name` | string | no | up to 200 characters | | `audience.people[].fields` | object | no | | | `audience.people[].tags` | list of string | no | up to 20 | | `audience.people[].values` | object | no | | | `audience.values` | object | no | | | `excludeOpen` | boolean | no | | | `frequencyDays` | integer | no | up to 90; null | | `startAt` | string (date-time) | no | null | | `sendWindow` | list of object | no | up to 14 | | `sendWindow[].day` | integer | yes | up to 7; at least 1 | | `sendWindow[].from` | string | yes | | | `sendWindow[].to` | string | yes | | | `reply` | object | no | | | `reply.kind` | one of `team`, `flow`, `assistant` | no | default `"team"` | | `reply.id` | string (uuid) | no | default `null`; null | | `reply.goal` | string | no | up to 200 characters; default `null`; null | | `successDays` | integer | no | up to 60; at least 1 | | `stopRules` | object | no | | | `stopRules.templatePaused` | boolean | no | default `true` | | `stopRules.qualityLow` | boolean | no | default `true` | | `stopRules.optoutRate` | number | no | up to 0.5; at least 0.005; default `0.03`; null | | `stopRules.budget` | boolean | no | default `true` | | `budgetMicros` | integer | no | up to 1000000000000000; null | ### Request example ```bash curl -X PATCH "https://api.donetalk.com/v1/c/demo/campaigns/:id" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Reativação · outubro (2)", "frequencyDays": 7 }' ``` ### Response 200 ```json { "campaign": { "id": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "name": "Reativação · outubro", "status": "draft", "channel": { "id": "01a0cccc-0000-7000-8000-000000000002", "name": "Captei" }, "template": { "id": "01a0cad0-0000-7000-8000-000000000002", "name": "reativacao_cupom", "category": "marketing" }, "variables": [ { "slot": "body.1", "source": "first_name", "format": "none", "fallback": "tudo bem" }, { "slot": "body.2", "source": "value.cupom", "format": "upper", "fallback": "" } ], "buttons": [ { "index": 0, "action": "route" } ], "audience": { "kind": "contacts", "contactIds": [ "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "01a0d05a-3ee3-75bd-9db5-dc9856f079c9" ] }, "excludeOpen": true, "frequencyDays": 7, "startAt": "2026-10-24T12:00:00.000Z", "sendWindow": [ { "day": 1, "from": "09:00", "to": "18:00" } ], "reply": { "kind": "team", "id": "01a0cb22-89c0-7e02-b96b-3f4731dde701", "goal": null }, "successDays": 7, "stopRules": { "templatePaused": true, "qualityLow": true, "optoutRate": 0.03, "budget": true }, "budgetMicros": null, "estimate": { "micros": 77824000, "currency": "BRL" }, "audienceCount": 1240, "header": null, "approval": { "required": false, "requestedTo": null, "requestedAt": null, "approvedBy": null, "approvedAt": null, "rejectedReason": null }, "createdBy": null, "createdByKind": "api", "startedAt": null, "paused": null, "completedAt": null, "createdAt": "2026-10-23T13:02:11.000Z", "updatedAt": "2026-10-23T13:02:11.000Z", "stats": { "audience": 1240, "queued": 0, "sending": 0, "sent": 1216, "delivered": 1198, "read": 874, "failed": 18, "skipped": 24, "replied": 141, "optedOut": 6, "successes": 37, "costMicros": 77824000, "followMicros": 1260000, "aiUsdMicros": 0 } } } ``` ### Possible errors - `400` `template_other_account` - `401` `unauthorized` - `403` `insufficient_scope` - `404` `campaign_not_found` - `409` `campaign_not_editable` - `422` `validation_failed` - `429` `rate_limited` ## Delete a campaign `DELETE /v1/c/{slug}/campaigns/{id}` · Scopes: `campaigns:write` Drafts, cancelled and completed ones. A sending campaign must be cancelled first. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X DELETE "https://api.donetalk.com/v1/c/demo/campaigns/:id" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "ok": true } ``` ### Possible errors - `400` `campaign_wrong_status` - `401` `unauthorized` - `403` `insufficient_scope` - `404` `campaign_not_found` - `422` `validation_failed` - `429` `rate_limited` ## Duplicate `POST /v1/c/{slug}/campaigns/{id}/duplicate` · Scopes: `campaigns:write` A new draft with the same setup and audience. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/campaigns/:id/duplicate" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 201 ```json { "campaign": { "id": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b" } } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `404` `campaign_not_found` - `422` `validation_failed` - `429` `rate_limited` ## Set the header image or document `PUT /v1/c/{slug}/campaigns/{id}/header` · Scopes: `campaigns:write` For templates with a media header: upload the file with `POST /uploads` and pass the `uploadId`. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `uploadId` | string (uuid) | yes | | ### Request example ```bash curl -X PUT "https://api.donetalk.com/v1/c/demo/campaigns/:id/header" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "uploadId": "01a3d1e2-f3a4-7b5c-8d6e-7f8091a2b3c4" }' ``` ### Response 200 ```json { "ok": true } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `404` `campaign_not_found` - `409` `campaign_not_editable` - `422` `validation_failed` - `429` `rate_limited` ## Audience preview `POST /v1/c/{slug}/campaigns/{id}/audience-preview` · Scopes: `campaigns:read` How many receive it and how many are left out, by reason: blocked, no WhatsApp, opted out, duplicate, marketing frequency, open conversation. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/campaigns/:id/audience-preview" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "found": 1264, "excluded": { "blocked": 0, "no_whatsapp": 2, "opted_out": 18, "duplicate": 0, "frequency": 4, "open_conversation": 0 }, "openConversations": 3, "receive": 1240, "samples": [ { "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "name": "Ana Souza", "address": "5548999990001" } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `404` `campaign_not_found` - `422` `validation_failed` - `429` `rate_limited` ## Message preview `POST /v1/c/{slug}/campaigns/{id}/message-preview` · Scopes: `campaigns:read` How the message reads for five people of the audience, and which variables would be empty. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/campaigns/:id/message-preview" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "slots": [ "body.1", "body.2" ], "quickReplies": [ { "index": 0, "text": "Quero o cupom" } ], "samples": [ { "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "name": "Ana Souza", "text": "Oi Ana! Seu cupom ANA10 vale até sexta. Quer usar agora?", "missing": [] } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `404` `campaign_not_found` - `422` `validation_failed` - `429` `rate_limited` ## Review: cost, checks and approval `POST /v1/c/{slug}/campaigns/{id}/review` · Scopes: `campaigns:read` The estimated cost (up to), the number's daily limit, the budgets, how many people would miss a variable, and whether the campaign will need approval. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/campaigns/:id/review" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "estimate": { "currency": "BRL", "people": 1240, "totalMicros": 77824000, "markets": [ { "market": "BR", "people": 1240, "rateMicros": 62760, "micros": 77822400 } ] }, "checks": { "templateApproved": true, "channelActive": true, "quality": "GREEN", "dailyLimit": 1000, "days": 2, "budgets": [], "campaignBudgetMicros": null, "missingVariables": 0 }, "approval": { "required": false, "approvers": [] } } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `404` `campaign_not_found` - `422` `validation_failed` - `429` `rate_limited` ## Send a test `POST /v1/c/{slug}/campaigns/{id}/test` · Scopes: `campaigns:write` Sends the message to one phone (yours), without counting in the campaign's numbers. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `phone` | string | yes | up to 40 characters | | `name` | string | no | up to 200 characters | | `values` | object | no | | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/campaigns/:id/test" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "phone": "+5548999990009", "name": "Leo", "values": { "cupom": "teste10" } }' ``` ### Response 200 ```json { "ok": true, "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "messageId": "01a3c0de-8b22-7c33-9d44-0e55f6677889" } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `404` `campaign_not_found` - `409` `template_not_approved` - `422` `validation_failed` - `429` `rate_limited` ## Start or schedule `POST /v1/c/{slug}/campaigns/{id}/submit` · Scopes: `campaigns:send` With a future `startAt` it is scheduled; without, it starts now. If the company's approval rule asks, the campaign waits as `pending_approval` until someone approves it in the app, unless the key has “Start without waiting for approval”. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `approverId` | string (uuid) | no | default `null`; null | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/campaigns/:id/submit" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` ### Response 200 ```json { "status": "scheduled" } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `404` `campaign_not_found` - `409` `campaign_not_editable` - `409` `template_not_approved` - `409` `campaign_audience_empty` - `422` `validation_failed` - `429` `rate_limited` ## Pause `POST /v1/c/{slug}/campaigns/{id}/pause` · Scopes: `campaigns:send` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/campaigns/:id/pause" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "status": "paused" } ``` ### Possible errors - `400` `campaign_wrong_status` - `401` `unauthorized` - `403` `insufficient_scope` - `404` `campaign_not_found` - `422` `validation_failed` - `429` `rate_limited` ## Resume `POST /v1/c/{slug}/campaigns/{id}/resume` · Scopes: `campaigns:send` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/campaigns/:id/resume" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "status": "running" } ``` ### Possible errors - `400` `campaign_wrong_status` - `401` `unauthorized` - `403` `insufficient_scope` - `404` `campaign_not_found` - `422` `validation_failed` - `429` `rate_limited` ## Cancel `POST /v1/c/{slug}/campaigns/{id}/cancel` · Scopes: `campaigns:send` Stops sending for good. What was sent still counts in the results. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/campaigns/:id/cancel" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "status": "cancelled" } ``` ### Possible errors - `400` `campaign_wrong_status` - `401` `unauthorized` - `403` `insufficient_scope` - `404` `campaign_not_found` - `422` `validation_failed` - `429` `rate_limited` ## Retry the failures `POST /v1/c/{slug}/campaigns/{id}/retry` · Scopes: `campaigns:send` Queues again those who failed: all, or only some contacts or error codes. Failures that cannot succeed (a number without WhatsApp, for example) stay out. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `contactIds` | list of string (uuid) | no | up to 20000 | | `errorCodes` | list of string | no | up to 50 | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/campaigns/:id/retry" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "errorCodes": [ "131049" ] }' ``` ### Response 200 ```json { "requeued": 12 } ``` ### Possible errors - `400` `campaign_wrong_status` - `401` `unauthorized` - `403` `insufficient_scope` - `404` `campaign_not_found` - `422` `validation_failed` - `429` `rate_limited` ## Recipients, one by one `GET /v1/c/{slug}/campaigns/{id}/recipients` · Scopes: `campaigns:read` Each contact with the message's status, the reply, the button tapped and the conversation it opened, with its outcome. `status` filters (`sent`, `delivered`, `read`, `failed`, `skipped`, `replied`, `success`); `format=csv` downloads everything. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Query parameters | Field | Type | Required | Notes | |---|---|---|---| | `status` | string | no | up to 20 characters | | `limit` | integer | no | up to 500; at least 1; default `100` | | `offset` | integer | no | default `0` | | `format` | one of `json`, `csv` | no | default `"json"` | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/campaigns/:id/recipients" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "recipients": [ { "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "name": "Ana Souza", "externalRef": "erp-88213", "address": "5548999990001", "status": "read", "skipReason": null, "errorCode": null, "sentAt": "2026-10-24T12:00:04.000Z", "deliveredAt": "2026-10-24T12:00:06.000Z", "readAt": "2026-10-24T12:03:51.000Z", "repliedAt": "2026-10-24T12:04:02.000Z", "buttonIndex": 0, "optedOutAt": null, "successAt": null, "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "nextAttemptAt": null, "conversation": { "status": "closed", "handler": "human", "lastInboundAt": "2026-10-24T12:09:10.000Z", "lastMessageAt": "2026-10-24T12:20:41.000Z", "lastMessageBy": "member", "closedAt": "2026-10-24T12:20:41.000Z", "closedBy": "member", "outcome": { "code": "converted", "category": "success", "label": { "pt-BR": "Venda", "en": "Sale", "es": "Venta" }, "note": "Fechou o plano anual com o cupom ANA10." } }, "tags": [ { "id": "01a12150-3610-7c00-b680-188c2b4c7313", "name": { "pt-BR": "VIP", "en": "VIP", "es": "VIP" } } ] } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `404` `campaign_not_found` - `422` `validation_failed` - `429` `rate_limited` ## Failures, buttons and clicks `GET /v1/c/{slug}/campaigns/{id}/insights` · Scopes: `campaigns:read` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/campaigns/:id/insights" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "failures": [ { "code": "131026", "count": 9, "waiting": 0 } ], "skipped": [ { "reason": "opted_out", "count": 18 } ], "buttons": [ { "index": 0, "text": "Quero o cupom", "taps": 96 } ], "links": [], "perHour": [ { "hour": "2026-10-24T12:00:00.000Z", "count": 640 } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `404` `campaign_not_found` - `422` `validation_failed` - `429` `rate_limited` --- # Segments ## List segments `GET /v1/c/{slug}/segments` · Scopes: `contacts:read` · `campaigns:write` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/segments" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "segments": [ { "id": "01a1a0b0-1c2d-7e3f-8a9b-0c1d2e3f4a5b", "name": "Clientes do plano anual", "conditions": [ { "field": "field.plano", "op": "eq", "value": "anual" } ], "count": 312, "updatedAt": "2026-10-23T13:02:11.000Z" } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Create a segment `POST /v1/c/{slug}/segments` · Scopes: `contacts:write` A group of contacts by conditions: tag, source, last outcome, days since the last conversation or since created, marketing consent, name, WhatsApp and any field (`field.`). ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `name` | string | yes | up to 120 characters | | `conditions` | list of object | yes | up to 20 | | `conditions[].field` | string | yes | up to 80 characters | | `conditions[].op` | one of `eq`, `neq`, `has`, `not_has`, `contains`, `empty`, `not_empty`, `gt`, `lt`, `none` | yes | | | `conditions[].value` | string · number · boolean · one of `null` | no | | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/segments" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Clientes do plano anual", "conditions": [ { "field": "field.plano", "op": "eq", "value": "anual" } ] }' ``` ### Response 201 ```json { "segment": { "id": "01a1a0b0-1c2d-7e3f-8a9b-0c1d2e3f4a5b" } } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Edit a segment `PUT /v1/c/{slug}/segments/{id}` · Scopes: `contacts:write` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `name` | string | yes | up to 120 characters | | `conditions` | list of object | yes | up to 20 | | `conditions[].field` | string | yes | up to 80 characters | | `conditions[].op` | one of `eq`, `neq`, `has`, `not_has`, `contains`, `empty`, `not_empty`, `gt`, `lt`, `none` | yes | | | `conditions[].value` | string · number · boolean · one of `null` | no | | ### Request example ```bash curl -X PUT "https://api.donetalk.com/v1/c/demo/segments/:id" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Clientes do plano anual", "conditions": [ { "field": "field.plano", "op": "eq", "value": "anual" } ] }' ``` ### Response 200 ```json { "ok": true } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Delete a segment `DELETE /v1/c/{slug}/segments/{id}` · Scopes: `contacts:write` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X DELETE "https://api.donetalk.com/v1/c/demo/segments/:id" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "ok": true } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Count who matches `POST /v1/c/{slug}/segments/count` · Scopes: `contacts:read` · `campaigns:write` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `conditions` | list of object | yes | up to 20 | | `conditions[].field` | string | yes | up to 80 characters | | `conditions[].op` | one of `eq`, `neq`, `has`, `not_has`, `contains`, `empty`, `not_empty`, `gt`, `lt`, `none` | yes | | | `conditions[].value` | string · number · boolean · one of `null` | no | | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/segments/count" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "conditions": [ { "field": "tag", "op": "has", "value": "01a12150-3610-7c00-b680-188c2b4c7313" } ] }' ``` ### Response 200 ```json { "count": 312 } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Search contacts by conditions `POST /v1/c/{slug}/contacts/search` · Scopes: `contacts:read` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `segmentId` | string (uuid) | no | | | `conditions` | list of object | no | up to 20; default `[]` | | `conditions[].field` | string | yes | up to 80 characters | | `conditions[].op` | one of `eq`, `neq`, `has`, `not_has`, `contains`, `empty`, `not_empty`, `gt`, `lt`, `none` | yes | | | `conditions[].value` | string · number · boolean · one of `null` | no | | | `q` | string | no | up to 100 characters | | `limit` | integer | no | up to 200; at least 1; default `50` | | `offset` | integer | no | up to 1000000; default `0` | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/contacts/search" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "conditions": [ { "field": "marketing", "op": "eq", "value": "opted_in" } ], "limit": 50 }' ``` ### Response 200 ```json { "total": 312, "marketingAllowed": 298, "contacts": [ { "id": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "displayName": "Ana Souza", "source": "api", "fields": { "cpf": "529.982.247-25", "plano": "anual" }, "isBlocked": false, "address": "5548999990001", "marketing": "opted_in", "lastOutcome": "converted", "tags": [ { "id": "01a12150-3610-7c00-b680-188c2b4c7313", "name": { "pt-BR": "VIP", "en": "VIP", "es": "VIP" }, "color": "#92400e" } ], "lastActivityAt": "2026-10-22T18:40:02.000Z", "createdAt": "2026-09-23T22:19:30.396Z" } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` --- # Contacts ## List contacts `GET /v1/c/{slug}/contacts` · Scopes: `contacts:read` Most active first. `q` searches name or phone; `tag` filters by tag; `before` (the last one's `lastActivityAt`) brings the next page. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Query parameters | Field | Type | Required | Notes | |---|---|---|---| | `q` | string | no | up to 200 characters | | `limit` | integer | no | up to 100; at least 1; default `50` | | `before` | string (date-time) | no | | | `tag` | string (uuid) | no | | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/contacts" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "contacts": [ { "id": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "displayName": "Ana Souza", "address": "5548999990001", "channelKind": "whatsapp", "source": "api", "externalRef": "erp-88213", "fields": { "cpf": "529.982.247-25", "plano": "anual" }, "isBlocked": false, "lastActivityAt": "2026-10-22T18:40:02.000Z", "createdAt": "2026-09-23T22:19:30.396Z", "tags": [ { "id": "01a12150-3610-7c00-b680-188c2b4c7313", "name": { "pt-BR": "VIP", "en": "VIP", "es": "VIP" }, "color": "#92400e" } ] } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Create a contact `POST /v1/c/{slug}/contacts` · Scopes: `contacts:write` To create or update many at once (or one that may exist), use `POST /contacts/import`. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `displayName` | string | yes | up to 200 characters | | `phone` | string | yes | | | `locale` | one of `pt-BR`, `es`, `en` | no | | | `fields` | object | no | | | `externalRef` | string | no | up to 200 characters | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/contacts" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "displayName": "Ana Souza", "phone": "+5548999990001", "externalRef": "erp-88213", "fields": { "cpf": "529.982.247-25" } }' ``` ### Response 201 ```json { "contact": { "id": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "displayName": "Ana Souza", "address": "5548999990001" } } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `409` `contact_exists` - `422` `validation_failed` - `429` `rate_limited` ## Create or update contacts (up to 1,000) `POST /v1/c/{slug}/contacts/import` · Scopes: `contacts:write` By phone: existing contacts are updated (name, fields, tags), new ones created. Each row comes back with the contact's id. A bad row does not stop the others. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `rows` | list of object | yes | up to 1000 | | `rows[].phone` | string | yes | up to 40 characters | | `rows[].name` | string | no | up to 200 characters | | `rows[].fields` | object | no | | | `rows[].tags` | list of string | no | up to 20 | | `rows[].externalRef` | string | no | up to 200 characters | | `rows[].marketingOptIn` | boolean | no | | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/contacts/import" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "rows": [ { "phone": "+5548999990001", "name": "Ana Souza", "tags": [ "VIP" ], "externalRef": "erp-88213" }, { "phone": "48 99999-0002", "name": "Bruno Lima", "fields": { "plano": "mensal" }, "marketingOptIn": true } ] }' ``` ### Response 200 ```json { "created": 1, "updated": 1, "errors": [ { "row": 2, "reason": "invalid_phone" } ], "contacts": [ { "row": 0, "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "created": false }, { "row": 1, "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c9", "created": true } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Get a contact `GET /v1/c/{slug}/contacts/{id}` · Scopes: `contacts:read` The contact with its fields, numbers, consents, last conversations and tags, plus the company's field definitions. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/contacts/:id" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "contact": { "id": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "displayName": "Ana Souza", "address": "5548999990001", "channelKind": "whatsapp", "source": "api", "externalRef": "erp-88213", "fields": { "cpf": "529.982.247-25", "plano": "anual" }, "isBlocked": false, "lastActivityAt": "2026-10-22T18:40:02.000Z", "createdAt": "2026-09-23T22:19:30.396Z", "firstName": "Ana", "lastName": "Souza", "locale": "pt-BR", "timezone": null, "blockedReason": null }, "identities": [ { "id": "01a0d05a-3ee3-75bd-9db5-dc9856f079c9", "channel_kind": "whatsapp", "address": "5548999990001", "external_id": null, "provider_display_name": "Ana", "is_primary": true, "last_seen_at": "2026-10-22T18:40:02.000Z" } ], "consents": [ { "purpose": "marketing", "channel_kind": "whatsapp", "status": "opted_in", "source": "api", "occurred_at": "2026-10-01T10:00:00.000Z" } ], "conversations": [ { "id": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "status": "closed", "source_kind": "campaign", "imported_from": null, "created_at": "2026-10-24T12:00:04.000Z", "closed_at": "2026-10-24T12:20:41.000Z", "channel_id": "01a0cccc-0000-7000-8000-000000000002", "outcome_code": "converted", "is_success": true } ], "tags": [ { "id": "01a12150-3610-7c00-b680-188c2b4c7313", "name": { "pt-BR": "VIP", "en": "VIP", "es": "VIP" }, "color": "#92400e" } ], "fieldDefinitions": [ { "key": "cpf", "label": { "pt-BR": "CPF", "en": "CPF", "es": "CPF" }, "type": "cpf", "options": null, "ai_extractable": true, "required": false, "sort": 1 } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `404` `contact_not_found` - `422` `validation_failed` - `429` `rate_limited` ## Edit a contact `PATCH /v1/c/{slug}/contacts/{id}` · Scopes: `contacts:write` `fields` merges with the contact's fields; send `null` in a field to clear it. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `displayName` | string | no | up to 200 characters | | `firstName` | string | no | up to 100 characters; null | | `lastName` | string | no | up to 100 characters; null | | `locale` | one of `pt-BR`, `es`, `en` | no | null | | `externalRef` | string | no | up to 200 characters; null | | `fields` | object | no | | | `isBlocked` | boolean | no | | | `blockedReason` | string | no | up to 200 characters; null | ### Request example ```bash curl -X PATCH "https://api.donetalk.com/v1/c/demo/contacts/:id" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "fields": { "plano": "anual" }, "externalRef": "erp-88213" }' ``` ### Response 200 ```json { "ok": true } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `404` `contact_not_found` - `422` `validation_failed` - `429` `rate_limited` ## Record consent `POST /v1/c/{slug}/contacts/{id}/consents` · Scopes: `contacts:write` Opt-in or opt-out per purpose (service, utility, marketing). A marketing opt-out keeps the contact out of marketing campaigns. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `purpose` | one of `service`, `utility`, `marketing` | yes | | | `status` | one of `opted_in`, `opted_out` | yes | | | `source` | one of `form`, `import`, `api`, `message`, `keyword` | no | default `"form"` | | `evidence` | object | no | | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/contacts/:id/consents" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "purpose": "marketing", "status": "opted_in", "source": "api" }' ``` ### Response 201 ```json { "ok": true } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `404` `contact_not_found` - `422` `validation_failed` - `429` `rate_limited` ## Add a tag `PUT /v1/c/{slug}/contacts/{id}/tags/{tagId}` · Scopes: `contacts:write` May start the flows that begin when this tag is added. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | | `tagId` | The tag's id. | ### Request example ```bash curl -X PUT "https://api.donetalk.com/v1/c/demo/contacts/:id/tags/:tagId" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "ok": true } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Remove a tag `DELETE /v1/c/{slug}/contacts/{id}/tags/{tagId}` · Scopes: `contacts:write` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | | `tagId` | The tag's id. | ### Request example ```bash curl -X DELETE "https://api.donetalk.com/v1/c/demo/contacts/:id/tags/:tagId" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "ok": true } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Context cards `GET /v1/c/{slug}/contacts/{id}/context-cards` · Scopes: `contacts:read` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/contacts/:id/context-cards" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "cards": [ { "key": "erp", "source": "ERP Captei", "title": "Cliente desde 2023", "status": "Plano anual", "tone": "success", "fields": [ { "label": "Pedidos", "value": "14" } ], "link": { "url": "https://erp.captei.com.br/clientes/88213", "label": "Abrir no ERP" }, "updatedAt": "2026-10-23T13:02:11.000Z" } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Show a card to the attendant `PUT /v1/c/{slug}/contacts/{id}/context-cards/{key}` · Scopes: `contacts:write` A card from your system shown beside the conversation (plan, orders, balance). The same `key` replaces the previous one. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | | `key` | The key (of the field or the card). | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `title` | string | yes | up to 120 characters | | `source` | string | no | up to 60 characters | | `status` | string | no | up to 60 characters; null | | `tone` | one of `neutral`, `success`, `warning`, `danger`, `info` | no | default `"neutral"` | | `fields` | list of object | no | up to 12; default `[]` | | `fields[].label` | string | yes | up to 60 characters | | `fields[].value` | string | yes | up to 300 characters | | `link` | object | no | null | | `link.url` | string (uri) | yes | up to 2000 characters | | `link.label` | string | no | up to 60 characters | ### Request example ```bash curl -X PUT "https://api.donetalk.com/v1/c/demo/contacts/:id/context-cards/:key" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "title": "Cliente desde 2023", "source": "ERP Captei", "status": "Plano anual", "tone": "success", "fields": [ { "label": "Pedidos", "value": "14" } ], "link": { "url": "https://erp.captei.com.br/clientes/88213", "label": "Abrir no ERP" } }' ``` ### Response 200 ```json { "card": { "key": "erp", "source": "ERP Captei", "title": "Cliente desde 2023", "status": "Plano anual", "tone": "success", "fields": [ { "label": "Pedidos", "value": "14" } ], "link": { "url": "https://erp.captei.com.br/clientes/88213", "label": "Abrir no ERP" }, "updatedAt": "2026-10-23T13:02:11.000Z" } } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Remove a card `DELETE /v1/c/{slug}/contacts/{id}/context-cards/{key}` · Scopes: `contacts:write` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | | `key` | The key (of the field or the card). | ### Request example ```bash curl -X DELETE "https://api.donetalk.com/v1/c/demo/contacts/:id/context-cards/:key" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "ok": true } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Erase the contact's data (LGPD) `POST /v1/c/{slug}/contacts/{id}/erase` · Scopes: `contacts:delete` At the person's request: the contact and their messages are anonymised for good. It cannot be undone. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/contacts/:id/erase" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "ok": true } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `404` `contact_not_found` - `422` `validation_failed` - `429` `rate_limited` --- # Contact fields ## List the contact fields `GET /v1/c/{slug}/contact-fields` · Scopes: `fields:read` · `contacts:read` The company's fields (key, name in three languages, type, options). Each contact's values come in the contact's `fields`. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/contact-fields" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "fields": [ { "key": "cpf", "label": { "pt-BR": "CPF", "en": "CPF", "es": "CPF" }, "type": "cpf", "options": null, "aiExtractable": true, "aiExtractionHint": null, "required": false, "sort": 1, "isActive": true } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Create a field `POST /v1/c/{slug}/contact-fields` · Scopes: `fields:write` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `key` | string | yes | | | `label` | string · object | yes | | | `type` | one of `text`, `number`, `date`, `boolean`, `select`, `phone`, `email`, `cpf`, `cnpj` | yes | | | `options` | list of string | no | up to 50; null | | `aiExtractable` | boolean | no | default `false` | | `aiExtractionHint` | string | no | up to 300 characters; null | | `required` | boolean | no | default `false` | | `sort` | integer | no | up to 1000 | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/contact-fields" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "key": "plano", "label": "Plano", "type": "select", "options": [ "mensal", "anual" ] }' ``` ### Response 201 ```json { "field": { "key": "cpf", "label": { "pt-BR": "CPF", "en": "CPF", "es": "CPF" }, "type": "cpf", "options": null, "aiExtractable": true, "aiExtractionHint": null, "required": false, "sort": 1, "isActive": true } } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Edit a field `PATCH /v1/c/{slug}/contact-fields/{key}` · Scopes: `fields:write` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `key` | The key (of the field or the card). | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `label` | string · object | no | | | `options` | list of string | no | up to 50; null | | `aiExtractable` | boolean | no | | | `aiExtractionHint` | string | no | up to 300 characters; null | | `required` | boolean | no | | | `sort` | integer | no | up to 1000 | | `isActive` | boolean | no | | ### Request example ```bash curl -X PATCH "https://api.donetalk.com/v1/c/demo/contact-fields/:key" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "options": [ "mensal", "semestral", "anual" ] }' ``` ### Response 200 ```json { "ok": true } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` --- # Templates ## List templates `GET /v1/c/{slug}/templates` · Scopes: `templates:read` · `campaigns:write` · `messages:send_template` The message templates of each WhatsApp account, with Meta's status, category and components (the `{{1}}` variables are in the text). ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/templates" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "templates": [ { "id": "01a0cad0-0000-7000-8000-000000000002", "name": "reativacao_cupom", "language": "pt_BR", "category": "marketing", "status": "approved", "quality": "GREEN", "accountId": "01a0cccc-0000-7000-8000-000000000001", "accountName": "Captei WABA", "components": [ { "type": "BODY", "text": "Oi {{1}}! Seu cupom {{2}} vale até sexta. Quer usar agora?" }, { "type": "BUTTONS", "buttons": [ { "type": "QUICK_REPLY", "text": "Quero o cupom" } ] } ], "rejectionReason": null, "updatedAt": "2026-10-23T13:02:11.000Z" } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` --- # Messages ## Send a template to a phone `POST /v1/c/{slug}/messages/template` · Scopes: `messages:send_template` In the contact's open conversation, or a new one handled the way the number handles what arrives. The contact is created if missing. `idempotencyKey` (or the Idempotency-Key header) prevents sending twice. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `phone` | string | yes | | | `contactName` | string | no | up to 200 characters | | `channelId` | string (uuid) | no | | | `templateId` | string (uuid) | yes | | | `body` | list of string | no | up to 20; default `[]` | | `header` | list of string | no | up to 5; default `[]` | | `buttons` | object | no | default `{}` | | `handling` | one of `number`, `team` | no | default `"number"` | | `assigneeEmail` | string (email) | no | up to 320 characters | | `idempotencyKey` | string | yes | up to 200 characters | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/messages/template" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "phone": "+5548999990001", "contactName": "Ana Souza", "templateId": "01a0cad0-0000-7000-8000-000000000002", "body": [ "Ana", "ANA10" ], "idempotencyKey": "pedido-88213-lembrete" }' ``` ### Response 201 ```json { "message": { "id": "01a3c0de-8b22-7c33-9d44-0e55f6677889", "status": "queued" }, "conversation": { "id": "01a3c0de-7a11-7b22-8c33-9d44e55f6677" }, "replay": false } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `409` `template_not_approved` - `422` `validation_failed` - `429` `rate_limited` ## A message's status and reply `GET /v1/c/{slug}/messages/{id}` · Scopes: `conversations:read` · `messages:send_template` · `messages:send_text` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/messages/:id" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "message": { "id": "01a3c0de-8b22-7c33-9d44-0e55f6677889", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "channelId": "01a0cccc-0000-7000-8000-000000000002", "direction": "outbound", "from": null, "type": "template", "text": "Oi Ana! Seu cupom ANA10 vale até sexta. Quer usar agora?", "status": "read", "errorCode": null, "createdAt": "2026-10-23T13:02:11.000Z", "sentAt": "2026-10-23T13:02:12.000Z", "deliveredAt": "2026-10-23T13:02:13.000Z", "readAt": "2026-10-23T13:05:40.000Z", "reply": { "id": "01a0d05a-3ee3-75bd-9db5-dc9856f079c9", "type": "button", "text": "Quero o cupom", "at": "2026-10-23T13:05:52.000Z" } } } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## A phone's messages `GET /v1/c/{slug}/messages` · Scopes: `conversations:read` A phone's messages since a moment, newest first (with or without Brazil's ninth digit). ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Query parameters | Field | Type | Required | Notes | |---|---|---|---| | `phone` | string | yes | | | `since` | string (date-time) | yes | | | `channelId` | string (uuid) | no | | | `direction` | one of `inbound`, `outbound` | no | | | `limit` | integer | no | up to 100; at least 1; default `20` | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/messages" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "messages": [ { "id": "01a3c0de-8b22-7c33-9d44-0e55f6677889", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "channelId": "01a0cccc-0000-7000-8000-000000000002", "direction": "outbound", "from": null, "type": "template", "text": "Oi Ana! Seu cupom ANA10 vale até sexta. Quer usar agora?", "status": "read", "errorCode": null, "createdAt": "2026-10-23T13:02:11.000Z", "sentAt": "2026-10-23T13:02:12.000Z", "deliveredAt": "2026-10-23T13:02:13.000Z", "readAt": "2026-10-23T13:05:40.000Z" } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` --- # Conversations ## List conversations `GET /v1/c/{slug}/conversations` · Scopes: `conversations:read` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Query parameters | Field | Type | Required | Notes | |---|---|---|---| | `view` | one of `mine`, `team`, `queue`, `waiting_contact`, `snoozed`, `closed`, `mentions`, `all` | no | default `"mine"` | | `limit` | integer | no | up to 100; at least 1; default `50` | | `before` | string (date-time) | no | | | `q` | string | no | up to 200 characters | | `tag` | string (uuid) | no | | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/conversations" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "conversations": [ { "id": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "status": "active", "handler": "human", "priority": "normal", "teamId": "01a0cb22-89c0-7e02-b96b-3f4731dde701", "assigneeId": "01a0cac1-4a6f-7646-b5f8-c1d19090016a", "unreadCount": 1, "lastMessageAt": "2026-10-23T13:05:52.000Z", "lastInboundAt": "2026-10-23T13:05:52.000Z", "sessionWindowEndsAt": "2026-10-24T13:05:52.000Z", "snoozedUntil": null, "sourceKind": "campaign", "createdAt": "2026-10-23T13:02:11.000Z", "tags": [ { "id": "01a12150-3610-7c00-b680-188c2b4c7313", "name": { "pt-BR": "VIP", "en": "VIP", "es": "VIP" }, "color": "#92400e" } ], "contact": { "id": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "name": "Ana Souza", "address": "5548999990001" }, "channel": { "id": "01a0cccc-0000-7000-8000-000000000002", "name": "Captei" }, "lastMessage": { "body": "Quero o cupom", "direction": "inbound", "type": "button", "status": "received" } } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Open a conversation with a template `POST /v1/c/{slug}/conversations` · Scopes: `messages:send_template` For an existing contact. `assigneeMemberId` picks who handles it; without one, it waits for the reply and joins the team's queue. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `contactId` | string (uuid) | yes | | | `channelId` | string (uuid) | no | | | `templateId` | string (uuid) | yes | | | `parameters` | object | no | default `{}` | | `headerUploadId` | string (uuid) | no | | | `headerLink` | string (uri) | no | up to 2000 characters | | `teamId` | string (uuid) | no | null | | `goal` | string | no | up to 60 characters | | `assigneeMemberId` | string (uuid) | no | | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/conversations" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "templateId": "01a0cad0-0000-7000-8000-000000000002", "parameters": { "1": "Ana" } }' ``` ### Response 201 ```json { "conversation": { "id": "01a3c0de-7a11-7b22-8c33-9d44e55f6677" }, "message": { "id": "01a3c0de-8b22-7c33-9d44-0e55f6677889" } } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `404` `contact_not_found` - `409` `template_not_approved` - `422` `validation_failed` - `429` `rate_limited` ## Get a conversation `GET /v1/c/{slug}/conversations/{id}` · Scopes: `conversations:read` The conversation with its messages, events (assignments, transfers) and, once closed, the outcome. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/conversations/:id" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "conversation": { "id": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "status": "active", "handler": "human", "priority": "normal", "teamId": "01a0cb22-89c0-7e02-b96b-3f4731dde701", "assigneeId": "01a0cac1-4a6f-7646-b5f8-c1d19090016a", "unreadCount": 1, "lastMessageAt": "2026-10-23T13:05:52.000Z", "lastInboundAt": "2026-10-23T13:05:52.000Z", "sessionWindowEndsAt": "2026-10-24T13:05:52.000Z", "snoozedUntil": null, "sourceKind": "campaign", "createdAt": "2026-10-23T13:02:11.000Z", "tags": [ { "id": "01a12150-3610-7c00-b680-188c2b4c7313", "name": { "pt-BR": "VIP", "en": "VIP", "es": "VIP" }, "color": "#92400e" } ], "contact": { "id": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "name": "Ana Souza", "address": "5548999990001" }, "channel": { "id": "01a0cccc-0000-7000-8000-000000000002", "name": "Captei" }, "lastMessage": { "body": "Quero o cupom", "direction": "inbound", "type": "button", "status": "received" }, "goal": null, "outcome": null, "closedAt": null }, "messages": [ { "id": "01a3c0de-8b22-7c33-9d44-0e55f6677889", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "direction": "outbound", "author": { "kind": "campaign", "id": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b" }, "type": "template", "body": "Oi Ana! Seu cupom ANA10 vale até sexta. Quer usar agora?", "status": "read", "error": null, "campaignId": "01a2b3c4-5d6e-7f80-9a1b-2c3d4e5f6a7b", "createdAt": "2026-10-23T13:02:11.000Z" } ], "pinned": [], "events": [] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Send a message in the conversation `POST /v1/c/{slug}/conversations/{id}/messages` · Scopes: `messages:send_template` · `messages:send_text` `type: template` with the messages:send_template scope. `text` and `media` (a file from `POST /uploads`) need messages:send_text and the 24-hour window open. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `type` | one of `text` | yes | | | `text` | string | yes | up to 4096 characters | | `replyToProviderMessageId` | string | no | | | `linkPreview` | boolean | no | | | `gateDecisionId` | string (uuid) | no | | | `gateAction` | one of `accepted`, `edited`, `overridden` | no | | | `templateId` | string (uuid) | yes | | | `parameters` | object | no | default `{}` | | `headerUploadId` | string (uuid) | no | | | `headerLink` | string (uri) | no | up to 2000 characters | | `uploadId` | string (uuid) | yes | | | `caption` | string | no | up to 1024 characters | | `contactIds` | list of string (uuid) | yes | up to 10 | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/conversations/:id/messages" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "text", "text": "Oi Ana! Seu pedido saiu para entrega." }' ``` ### Response 202 ```json { "message": { "id": "01a3c0de-8b22-7c33-9d44-0e55f6677889", "status": "queued", "createdAt": "2026-10-23T13:02:11.000Z", "media": null } } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `403` `integration_template_only` - `409` `window_closed` - `422` `validation_failed` - `429` `rate_limited` ## Close with an outcome `POST /v1/c/{slug}/conversations/{id}/close` · Scopes: `conversations:write` `outcomeCode` comes from `GET /outcomes`. With `ifNoReplyHours`, it closes only if the contact does not answer in that time. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `outcomeCode` | string | yes | up to 60 characters | | `note` | string | no | up to 4000 characters | | `ifNoReplyHours` | integer | no | up to 720; at least 1 | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/conversations/:id/close" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "outcomeCode": "converted", "note": "Fechou o plano anual." }' ``` ### Response 200 ```json { "ok": true, "isSuccess": true } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Reopen `POST /v1/c/{slug}/conversations/{id}/reopen` · Scopes: `conversations:write` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/conversations/:id/reopen" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "ok": true } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Upload a file `POST /v1/c/{slug}/uploads` · Scopes: `messages:send_text` · `campaigns:write` · `messages:send_template` multipart/form-data with the `file` field (and `intent`: media, document, audio, sticker). Returns the `id` to send in a message or set as a campaign's header. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request body `multipart/form-data`: `file` + `intent` (media, document, audio, sticker). ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/uploads" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 201 ```json { "upload": { "id": "01a3d1e2-f3a4-7b5c-8d6e-7f8091a2b3c4", "mime": "image/jpeg", "sizeBytes": 182340, "filename": "cupom.jpg", "kind": "image", "voice": false, "conversion": null, "durationMs": null, "width": 1080, "height": 1080 } } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` --- # Numbers, teams and tags ## WhatsApp numbers `GET /v1/c/{slug}/channels` · Scopes: `workspace:read` · `campaigns:write` · `messages:send_template` · `templates:read` The company's numbers, with the quality and daily limit Meta gave each one. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/channels" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "channels": [ { "id": "01a0cccc-0000-7000-8000-000000000002", "name": "Captei", "phone": "554896429898", "status": "active", "quality": "GREEN", "messagingTier": "TIER_1K", "account": { "id": "01a0cccc-0000-7000-8000-000000000001", "name": "Captei WABA" }, "currency": "BRL", "handling": { "mode": "flow", "teamId": "01a0cb22-89c0-7e02-b96b-3f4731dde701", "flowId": "01a0d60a-3fc1-7df5-a07c-9769fd5701e6", "assistantId": null }, "createdAt": "2026-09-22T20:14:21.670Z" } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Teams `GET /v1/c/{slug}/teams` · Scopes: `workspace:read` · `campaigns:write` To send a campaign's replies to the right team (`reply.id`). ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/teams" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "teams": [ { "id": "01a0cb22-89c0-7e02-b96b-3f4731dde701", "name": "Vendas", "description": null, "isActive": true, "distributionMode": "least_busy", "businessHoursId": null, "members": [ { "id": "01a0cac1-4a6f-7646-b5f8-c1d19090016a", "name": "Tayomara Batista", "presence": "online" } ] } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Tags `GET /v1/c/{slug}/tags` · Scopes: `workspace:read` · `contacts:read` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/tags" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "tags": [ { "id": "01a12150-3610-7c00-b680-188c2b4c7313", "name": { "pt-BR": "VIP", "en": "VIP", "es": "VIP" }, "color": "#92400e", "appliesTo": "both", "isActive": true, "contacts": 128, "conversations": 40 } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Outcomes `GET /v1/c/{slug}/outcomes` · Scopes: `workspace:read` · `conversations:read` · `conversations:write` How a conversation may close (sale, not interested, solved…), with the code used in `close` and in the events. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/outcomes" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "outcomes": [ { "id": "01a0cb22-0000-7000-8000-00000000000a", "code": "converted", "label": { "pt-BR": "Venda", "en": "Sale", "es": "Venta" }, "category": "success", "is_success": true, "requires_note": false, "reopen_allowed": true, "sort": 1 } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` --- # Scheduling ## Appointment types `GET /v1/c/{slug}/appointment-types` · Scopes: `appointments:read` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/appointment-types" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "types": [ { "id": "01a1228c-c58f-796a-b3aa-54a953711c71", "agendaId": "01a1228c-c584-73f7-b7e7-bfaa0d02fed0", "name": "Visita ao imóvel", "durationMin": 60, "agendaName": "Visitas aos imóveis", "timezone": "America/Sao_Paulo", "isActive": true } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Free times `GET /v1/c/{slug}/appointment-types/{id}/slots` · Scopes: `appointments:read` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Query parameters | Field | Type | Required | Notes | |---|---|---|---| | `from` | string (date-time) | no | | | `to` | string (date-time) | no | | | `limit` | integer | no | up to 500; at least 1; default `100` | | `contactId` | string (uuid) | no | | | `memberId` | string (uuid) | no | | | `exclude` | string (uuid) | no | | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/appointment-types/:id/slots" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "timezone": "America/Sao_Paulo", "durationMin": 60, "slots": [ { "start": "2026-10-27T13:00:00.000Z", "end": "2026-10-27T14:00:00.000Z", "memberIds": [ "01a0cac1-4a6f-7646-b5f8-c1d19090016a" ] } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Appointments `GET /v1/c/{slug}/appointments` · Scopes: `appointments:read` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Query parameters | Field | Type | Required | Notes | |---|---|---|---| | `from` | string (date-time) | no | | | `to` | string (date-time) | no | | | `agendaId` | string (uuid) | no | | | `memberId` | string (uuid) | no | | | `mine` | integer | no | up to 1; default `0` | | `status` | string | no | | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/appointments" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "appointments": [ { "id": "01a2f00d-1234-7abc-8def-0123456789ab", "agendaId": "01a1228c-c584-73f7-b7e7-bfaa0d02fed0", "agendaName": "Visitas aos imóveis", "typeId": "01a1228c-c58f-796a-b3aa-54a953711c71", "typeName": "Visita ao imóvel", "typeColor": "#1daa61", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "contactName": "Ana Souza", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "channelId": "01a0cccc-0000-7000-8000-000000000002", "memberId": "01a0cac1-4a6f-7646-b5f8-c1d19090016a", "memberName": "Tayomara Batista", "holderIds": [ "01a0cac1-4a6f-7646-b5f8-c1d19090016a" ], "status": "booked", "startsAt": "2026-10-27T13:00:00.000Z", "endsAt": "2026-10-27T14:00:00.000Z", "timezone": "America/Sao_Paulo", "location": "Rua Bahia, 120 – Higienópolis, São Paulo", "locationKind": "address", "onlineUrl": null, "answers": { "imovel": "AP-2231" }, "notes": null, "source": "api", "createdByKind": "api_key", "createdById": null, "confirmedAt": null, "cancelledAt": null, "cancelledByKind": null, "cancelReason": null, "rescheduleCount": 0, "previousStartsAt": null, "publicToken": "r3WqZ8", "createdAt": "2026-10-23T13:02:11.000Z" } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Book `POST /v1/c/{slug}/appointments` · Scopes: `appointments:write` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `typeId` | string (uuid) | yes | | | `contactId` | string (uuid) | yes | | | `startsAt` | string (date-time) | yes | | | `conversationId` | string (uuid) | no | null | | `memberId` | string (uuid) | no | null | | `answers` | object | no | | | `notes` | string | no | up to 2000 characters; null | | `force` | boolean | no | | | `confirm` | boolean | no | | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/appointments" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "typeId": "01a1228c-c58f-796a-b3aa-54a953711c71", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "startsAt": "2026-10-27T13:00:00.000Z", "answers": { "imovel": "AP-2231" } }' ``` ### Response 201 ```json { "appointment": { "id": "01a2f00d-1234-7abc-8def-0123456789ab", "agendaId": "01a1228c-c584-73f7-b7e7-bfaa0d02fed0", "agendaName": "Visitas aos imóveis", "typeId": "01a1228c-c58f-796a-b3aa-54a953711c71", "typeName": "Visita ao imóvel", "typeColor": "#1daa61", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "contactName": "Ana Souza", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "channelId": "01a0cccc-0000-7000-8000-000000000002", "memberId": "01a0cac1-4a6f-7646-b5f8-c1d19090016a", "memberName": "Tayomara Batista", "holderIds": [ "01a0cac1-4a6f-7646-b5f8-c1d19090016a" ], "status": "booked", "startsAt": "2026-10-27T13:00:00.000Z", "endsAt": "2026-10-27T14:00:00.000Z", "timezone": "America/Sao_Paulo", "location": "Rua Bahia, 120 – Higienópolis, São Paulo", "locationKind": "address", "onlineUrl": null, "answers": { "imovel": "AP-2231" }, "notes": null, "source": "api", "createdByKind": "api_key", "createdById": null, "confirmedAt": null, "cancelledAt": null, "cancelledByKind": null, "cancelReason": null, "rescheduleCount": 0, "previousStartsAt": null, "publicToken": "r3WqZ8", "createdAt": "2026-10-23T13:02:11.000Z" } } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Get an appointment `GET /v1/c/{slug}/appointments/{id}` · Scopes: `appointments:read` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/appointments/:id" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "appointment": { "id": "01a2f00d-1234-7abc-8def-0123456789ab", "agendaId": "01a1228c-c584-73f7-b7e7-bfaa0d02fed0", "agendaName": "Visitas aos imóveis", "typeId": "01a1228c-c58f-796a-b3aa-54a953711c71", "typeName": "Visita ao imóvel", "typeColor": "#1daa61", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "contactName": "Ana Souza", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "channelId": "01a0cccc-0000-7000-8000-000000000002", "memberId": "01a0cac1-4a6f-7646-b5f8-c1d19090016a", "memberName": "Tayomara Batista", "holderIds": [ "01a0cac1-4a6f-7646-b5f8-c1d19090016a" ], "status": "booked", "startsAt": "2026-10-27T13:00:00.000Z", "endsAt": "2026-10-27T14:00:00.000Z", "timezone": "America/Sao_Paulo", "location": "Rua Bahia, 120 – Higienópolis, São Paulo", "locationKind": "address", "onlineUrl": null, "answers": { "imovel": "AP-2231" }, "notes": null, "source": "api", "createdByKind": "api_key", "createdById": null, "confirmedAt": null, "cancelledAt": null, "cancelledByKind": null, "cancelReason": null, "rescheduleCount": 0, "previousStartsAt": null, "publicToken": "r3WqZ8", "createdAt": "2026-10-23T13:02:11.000Z" }, "notices": [], "events": [], "history": [] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Reschedule `POST /v1/c/{slug}/appointments/{id}/reschedule` · Scopes: `appointments:write` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `startsAt` | string (date-time) | yes | | | `memberId` | string (uuid) | no | null | | `force` | boolean | no | | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/appointments/:id/reschedule" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "startsAt": "2026-10-28T13:00:00.000Z" }' ``` ### Response 200 ```json { "appointment": { "id": "01a2f00d-1234-7abc-8def-0123456789ab", "agendaId": "01a1228c-c584-73f7-b7e7-bfaa0d02fed0", "agendaName": "Visitas aos imóveis", "typeId": "01a1228c-c58f-796a-b3aa-54a953711c71", "typeName": "Visita ao imóvel", "typeColor": "#1daa61", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "contactName": "Ana Souza", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "channelId": "01a0cccc-0000-7000-8000-000000000002", "memberId": "01a0cac1-4a6f-7646-b5f8-c1d19090016a", "memberName": "Tayomara Batista", "holderIds": [ "01a0cac1-4a6f-7646-b5f8-c1d19090016a" ], "status": "booked", "startsAt": "2026-10-27T13:00:00.000Z", "endsAt": "2026-10-27T14:00:00.000Z", "timezone": "America/Sao_Paulo", "location": "Rua Bahia, 120 – Higienópolis, São Paulo", "locationKind": "address", "onlineUrl": null, "answers": { "imovel": "AP-2231" }, "notes": null, "source": "api", "createdByKind": "api_key", "createdById": null, "confirmedAt": null, "cancelledAt": null, "cancelledByKind": null, "cancelReason": null, "rescheduleCount": 0, "previousStartsAt": null, "publicToken": "r3WqZ8", "createdAt": "2026-10-23T13:02:11.000Z" } } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Cancel `POST /v1/c/{slug}/appointments/{id}/cancel` · Scopes: `appointments:write` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `reason` | string | no | up to 300 characters; null | | `notify` | boolean | no | default `true` | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/appointments/:id/cancel" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "reason": "Cliente pediu" }' ``` ### Response 200 ```json { "appointment": { "id": "01a2f00d-1234-7abc-8def-0123456789ab", "agendaId": "01a1228c-c584-73f7-b7e7-bfaa0d02fed0", "agendaName": "Visitas aos imóveis", "typeId": "01a1228c-c58f-796a-b3aa-54a953711c71", "typeName": "Visita ao imóvel", "typeColor": "#1daa61", "contactId": "01a0d05a-3ee3-75bd-9db5-dc9856f079c8", "contactName": "Ana Souza", "conversationId": "01a3c0de-7a11-7b22-8c33-9d44e55f6677", "channelId": "01a0cccc-0000-7000-8000-000000000002", "memberId": "01a0cac1-4a6f-7646-b5f8-c1d19090016a", "memberName": "Tayomara Batista", "holderIds": [ "01a0cac1-4a6f-7646-b5f8-c1d19090016a" ], "status": "cancelled", "startsAt": "2026-10-27T13:00:00.000Z", "endsAt": "2026-10-27T14:00:00.000Z", "timezone": "America/Sao_Paulo", "location": "Rua Bahia, 120 – Higienópolis, São Paulo", "locationKind": "address", "onlineUrl": null, "answers": { "imovel": "AP-2231" }, "notes": null, "source": "api", "createdByKind": "api_key", "createdById": null, "confirmedAt": null, "cancelledAt": null, "cancelledByKind": null, "cancelReason": null, "rescheduleCount": 0, "previousStartsAt": null, "publicToken": "r3WqZ8", "createdAt": "2026-10-23T13:02:11.000Z" } } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` --- # Webhooks ## List webhooks `GET /v1/c/{slug}/webhooks` · Scopes: `webhooks:manage` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/webhooks" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "webhooks": [ { "id": "01a0cb03-417a-7da4-9ae2-70bc321eeef5", "url": "https://erp.captei.com.br/donetalk/avisos", "events": [ "campaign.completed", "campaign.recipient.replied", "conversation.closed" ], "status": "active", "failureCount": 0, "createdBy": null, "createdAt": "2026-10-23T13:02:11.000Z", "updatedAt": "2026-10-23T13:02:11.000Z" } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Create a webhook `POST /v1/c/{slug}/webhooks` · Scopes: `webhooks:manage` An address of your system and the events it receives. The `secret` comes back only once: keep it to check the signatures. ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `url` | string (uri) | yes | up to 2000 characters | | `events` | list of one of `conversation.opened`, `conversation.closed`, `conversation.assigned`, `conversation.transferred`, `conversation.reopened`, `contact.updated`, `contact.consent_changed`, `message.received`, `message.sent`, `message.delivered`, `message.read`, `message.failed`, `flow.event`, `appointment.booked`, `appointment.rescheduled`, `appointment.cancelled`, `appointment.confirmed`, `appointment.attended`, `appointment.no_show`, `campaign.started`, `campaign.paused`, `campaign.resumed`, `campaign.completed`, `campaign.cancelled`, `campaign.recipient.sent`, `campaign.recipient.delivered`, `campaign.recipient.read`, `campaign.recipient.replied`, `campaign.recipient.failed`, `campaign.recipient.opted_out` | yes | up to 30 | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/webhooks" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://erp.captei.com.br/donetalk/avisos", "events": [ "campaign.completed", "campaign.recipient.replied", "conversation.closed" ] }' ``` ### Response 201 ```json { "webhook": { "id": "01a0cb03-417a-7da4-9ae2-70bc321eeef5", "url": "https://erp.captei.com.br/donetalk/avisos", "events": [ "campaign.completed", "campaign.recipient.replied", "conversation.closed" ], "status": "active", "failureCount": 0, "createdBy": null, "createdAt": "2026-10-23T13:02:11.000Z", "updatedAt": "2026-10-23T13:02:11.000Z" }, "secret": "dsf_whsec_4Jx…" } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Edit a webhook `PATCH /v1/c/{slug}/webhooks/{id}` · Scopes: `webhooks:manage` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request body | Field | Type | Required | Notes | |---|---|---|---| | `url` | string (uri) | no | up to 2000 characters | | `events` | list of one of `conversation.opened`, `conversation.closed`, `conversation.assigned`, `conversation.transferred`, `conversation.reopened`, `contact.updated`, `contact.consent_changed`, `message.received`, `message.sent`, `message.delivered`, `message.read`, `message.failed`, `flow.event`, `appointment.booked`, `appointment.rescheduled`, `appointment.cancelled`, `appointment.confirmed`, `appointment.attended`, `appointment.no_show`, `campaign.started`, `campaign.paused`, `campaign.resumed`, `campaign.completed`, `campaign.cancelled`, `campaign.recipient.sent`, `campaign.recipient.delivered`, `campaign.recipient.read`, `campaign.recipient.replied`, `campaign.recipient.failed`, `campaign.recipient.opted_out` | no | up to 30 | | `status` | one of `active`, `paused` | no | | ### Request example ```bash curl -X PATCH "https://api.donetalk.com/v1/c/demo/webhooks/:id" \ -H "Authorization: Bearer $DONETALK_KEY" \ -H "Content-Type: application/json" \ -d '{ "status": "paused" }' ``` ### Response 200 ```json { "webhook": { "id": "01a0cb03-417a-7da4-9ae2-70bc321eeef5", "url": "https://erp.captei.com.br/donetalk/avisos", "events": [ "campaign.completed", "campaign.recipient.replied", "conversation.closed" ], "status": "active", "failureCount": 0, "createdBy": null, "createdAt": "2026-10-23T13:02:11.000Z", "updatedAt": "2026-10-23T13:02:11.000Z" } } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Delete a webhook `DELETE /v1/c/{slug}/webhooks/{id}` · Scopes: `webhooks:manage` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X DELETE "https://api.donetalk.com/v1/c/demo/webhooks/:id" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "ok": true } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Send a test event `POST /v1/c/{slug}/webhooks/{id}/test` · Scopes: `webhooks:manage` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X POST "https://api.donetalk.com/v1/c/demo/webhooks/:id/test" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 202 ```json { "deliveryId": "01a3e4f5-a6b7-7c8d-9e0f-1a2b3c4d5e6f", "eventId": "01a3c0de-8b22-7c33-9d44-0e55f6677889" } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited` ## Recent deliveries `GET /v1/c/{slug}/webhooks/{id}/deliveries` · Scopes: `webhooks:manage` ### Path parameters | Field | Notes | |---|---| | `slug` | The company's identifier in DoneTalk (the key's own). | | `id` | The record's id. | ### Request example ```bash curl -X GET "https://api.donetalk.com/v1/c/demo/webhooks/:id/deliveries" \ -H "Authorization: Bearer $DONETALK_KEY" ``` ### Response 200 ```json { "deliveries": [ { "id": "01a3e4f5-a6b7-7c8d-9e0f-1a2b3c4d5e6f", "eventId": "01a3c0de-8b22-7c33-9d44-0e55f6677889", "eventType": "campaign.recipient.replied", "attempt": 1, "status": "delivered", "responseCode": 200, "responseMs": 84, "nextAttemptAt": null, "createdAt": "2026-10-24T12:04:03.000Z" } ] } ``` ### Possible errors - `401` `unauthorized` - `403` `insufficient_scope` - `422` `validation_failed` - `429` `rate_limited`