Errors, limits and good practice
Error format
Every error comes back like this:
{
"error": "validation_failed",
"message": "A field is wrong. `details` says which and why.",
"details": { "name": ["Required"] },
"request_id": "01a3c0de-7a11-7b22-8c33-9d44e55f6677"
}erroris the stable code: your system decides what to do from it.messageexplains, in the language of theAccept-Languageheader (Portuguese, English or Spanish; English by default).request_ididentifies the call: quote it to support. You may send your own inX-Request-Id.
| HTTP | Code | What to do |
|---|---|---|
| 401 | unauthorized | The key is missing. Send Authorization: Bearer <key>. |
| 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:
RateLimit-Limit: 600
RateLimit-Remaining: 587
RateLimit-Reset: 41Past 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.
Idempotency-Key: order-88213-campaignPaging
- Campaigns:
limitandcursor; the answer carriesnextCursor(null on the last page). - Contacts:
limitandbefore(the page's lastlastActivityAt). - A campaign's recipients:
limitandoffset, orformat=csvfor 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.