# Errors & Status Codes
# Error Format
Every error response uses the same envelope:
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable description"
}
}
Validation errors include an extra details field with per-field messages:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "The given data was invalid",
"details": {
"email": ["The email field is required."],
"per_page": ["The per page must be between 1 and 100."]
}
}
}
# HTTP Status Codes
| Code | Meaning |
|---|---|
| 200 | Request succeeded |
| 201 | Resource created — returned by POST /contact |
| 400 | Bad request — invalid parameter format (e.g. malformed tag_ids list) |
| 401 | Unauthorized — missing or invalid API key |
| 403 | Forbidden — your key does not have access to this resource |
| 404 | Not found — the requested resource does not exist |
| 409 | Conflict — the resource already exists (e.g. duplicate contact email) |
| 422 | Unprocessable — validation failed; see error.details for field-level errors |
| 429 | Too many requests — you have exceeded the rate limit (600 requests/minute) |
| 500 | Server error — something went wrong on our end |
# Error Codes
| Code | HTTP | When it occurs |
|---|---|---|
UNAUTHORIZED | 401 | API key is missing, invalid, or disabled |
FORBIDDEN | 403 | The API key does not have access to the requested resource |
NOT_FOUND | 404 | Contact, tag, sequence (etc.) not found — or doesn't belong to your account |
CONTACT_EXISTS | 409 | POST /contact — a contact with that email already exists |
TAG_EXISTS | 409 | POST /tag or PUT /tag — a tag with that name already exists |
WEBHOOK_EXISTS | 409 | POST /webhook — a webhook with that URL already exists |
PATH_TAKEN | 409 | PUT /funnel-step — the requested URL slug is already used in this funnel |
CAMPAIGN_SCHEDULED | 409 | DELETE /campaign — campaign is scheduled and must be unscheduled first |
INVALID_STATUS | 400 | The status filter value is not in the allowed list |
INVALID_PERIOD | 400 | The period value is invalid or range is missing for period=between |
INVALID_DATE | 400 | scheduled_for could not be parsed or is in the past |
NO_SUBDOMAIN | 400 | POST /funnel — no subdomain is configured for this account |
STAGE_NOT_EMAIL | 422 | PUT /sequence-stage-content — the stage is not a send_email stage |
SEQUENCE_HAS_NO_STAGES | 422 | POST /contact-sequence — the sequence has no stages and cannot enrol contacts |
VALIDATION_ERROR | 400/422 | Validation failed. 400 when a list field (tag_ids, sequence_ids) is malformed — no details. 422 for standard field errors (missing email/contact_id, etc.) — includes details. |
SERVER_ERROR | 500 | Unexpected server-side error |
# Rate Limiting (429)
When you exceed 600 requests per minute (≈ 10 requests per second), the API responds with:
{
"message": "Too Many Attempts."
}
HTTP status: 429 Too Many Requests
Implement an exponential back-off or a request queue on your side to stay within limits.
# Tips
- Always check
successbefore readingdata. - On a 422, iterate over
error.detailsto surface per-field validation messages to your users. - On a 429, wait at least 60 seconds before retrying.
- On a 5xx, log the full response and retry after a short delay — these are transient.