Errors
Every failure uses one envelope:
{
"error": {
"code": "contact_already_exists",
"message": "A contact with this email already exists in this audience",
"param": "email",
"request_id": "req_8fk2..."
}
}codeis the contract. Branch on it. Never parsemessage.messageis for humans and may change at any time.paramnames the offending input field, when one is at fault.request_idis always present. Include it when reporting a problem to day3 support.
Codes
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | Malformed body, or a value that failed validation. |
| 400 | invalid_email | The address is not a valid email. |
| 400 | invalid_filter | A stored segment filter no longer parses, so its members cannot be listed. A filter rejected on write is invalid_request. |
| 400 | batch_too_large | More than 1,000 items in one batch. |
| 400 | email_suppressed | On POST /emails: the address hard-bounced, complained, or was suppressed by the provider. |
| 401 | invalid_api_key | Missing, malformed, or unknown key. |
| 401 | revoked_api_key | The key was revoked. |
| 403 | plan_limit_reached | Out of subscriber headroom, or out of monthly send allowance. |
| 403 | sending_disabled | Sending is turned off for this account. |
| 403 | domain_not_verified | The from domain is not a verified sending domain. |
| 403 | sandbox_recipient_not_allowed | Free plan, and the recipient is not a member of your organization. |
| 403 | insufficient_scope | The key lacks campaigns:send, automations:enroll or webhooks:manage. |
| 403 | test_keys_not_supported | A day3_test_ key was used. |
| 403 | forbidden | The action is not allowed for this key or account state. |
| 404 | not_found | No such object. Also returned for another organization’s ids. |
| 409 | contact_already_exists | Creating a contact whose email is already in the audience, without ?upsert=true. |
| 409 | email_suppressed | On contact writes: the address is on the account’s suppression list. |
| 409 | idempotency_conflict | Same Idempotency-Key, different body. Or the same key is already in flight. |
| 409 | invalid_request | The action conflicts with the object’s state: editing a campaign that has left draft, or enrolling into an unpublished automation. |
| 422 | immutable_field | Changing something that cannot change, such as a field’s key. |
| 429 | rate_limit_exceeded | Over the request limit. Carries Retry-After. |
| 500 | internal_error | Our fault. Retry, and send us the request_id. |
What to retry
| Code | Retry? |
|---|---|
rate_limit_exceeded | Yes, after Retry-After seconds. |
internal_error | Yes, with backoff. |
idempotency_conflict meaning “already in progress” | Yes, in a moment. |
plan_limit_reached | No. Tell the user to upgrade. |
domain_not_verified | No. Verify the domain first. |
email_suppressed | Never. The address bounced or complained. Retrying it damages your sending reputation. |
sandbox_recipient_not_allowed | No. Upgrade, or send to an organization member. |
insufficient_scope | No. Mint a key that has the scope. |
invalid_request, invalid_email | No. Fix the payload. param names the field. |
invalid_filter | No. Rewrite the segment’s filter in the app or over the API. |
Why email_suppressed appears twice
The same code shows up with two statuses, on purpose:
400onPOST /emails. You asked to send to an address that must not be mailed.409on contact writes. You asked to create a contact that conflicts with account-wide suppression state.
Both mean the address is unmailable. Neither should be retried.
Last updated on