Errors & troubleshooting
This page covers the most common API integration errors.
Error shape#
Most external API errors use this shape:
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable explanation"
}
}
Some low-level infrastructure errors may still appear differently while the API is evolving, but the goal is to return domain-specific 400/403/404 responses wherever possible.
Authentication and scope errors#
API_KEY_FORBIDDEN#
Meaning:
- the route is not part of the public API for service-account keys
A 403 here is not a scope problem and adding scopes will not fix it. The
capability is one the app keeps to itself; see
what service-account keys cannot do.
ACTION_NOT_AVAILABLE#
Meaning:
- the endpoint is public, but the specific action asked for is not available to API keys
Today this is reset_status on POST /api/contacts/bulk — see
bulk actions. Every other action on that endpoint
works normally.
INSUFFICIENT_SCOPE#
Meaning:
- the key is valid, but it lacks the required scope
Example:
{
"error": {
"code": "INSUFFICIENT_SCOPE",
"message": "This key is missing a required scope: broadcasts:schedule"
}
}
Broadcast validation errors#
INVALID_EMAIL_TYPE#
Meaning:
email_type_keywas not found for the tenant
INVALID_MTA_CONFIG#
Meaning:
mta_config_keywas not found for the tenant
INVALID_SEGMENT#
Meaning:
- one of the supplied segment keys was not found for the tenant
INVALID_SENDING_IDENTITY#
Meaning:
- one of the supplied sender email addresses was not found for the tenant
INVALID_TEMPLATE#
Meaning:
content.template_slugwas not found for the tenant
INVALID_METADATA#
Meaning:
- broadcast
metadatafails validation
Typical causes:
- key doesn’t match
/^[a-z][a-z0-9_]{0,63}$/ - nested objects in values
- more than 20 keys
- serialized size exceeds 8KB
INVALID_METADATA_FILTER#
Meaning:
- the
metadataquery parameter on the list endpoint is not valid JSON or not a plain object
INVALID_STATUS#
Meaning:
- the
statusquery parameter on the list endpoint contains an unrecognized status value
NO_CONTENT#
Meaning:
- required content fields are missing
Typical causes:
- missing
content.subject - missing both
content.body_htmlandcontent.body_mjml(unless using template-mode withtemplate_slug+template_binding)
INVALID_VARIANTS#
Meaning:
- the A/B variant payload is incomplete or malformed
Typical causes:
- fewer than 2 variants
- missing
id,name, or positiveweight - no meaningful content override on a variant
ADVANCED_SPLIT_TEST_UNSUPPORTED#
Meaning:
- the request used advanced split-test fields not supported in the public API
Rate limits#
Each API key gets its own budget of 1000 requests per minute. The limit is per key, not per workspace, so one integration cannot starve another — and a runaway loop in one place doesn’t take down the rest of your automation.
Every response carries the current state:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed in the window |
X-RateLimit-Remaining | Requests left |
X-RateLimit-Reset | Seconds until the window resets |
Exceeding it returns 429:
{
"statusCode": 429,
"error": {
"code": "RATE_LIMITED",
"message": "API key rate limit exceeded (1000 requests/minute). Retry after 42 seconds."
}
}
Back off using X-RateLimit-Reset rather than retrying immediately.
If you are hitting this, the usual cause is per-record requests where a bulk
endpoint exists. POST /api/contacts/bulk and PUT /api/contacts handle many
contacts per call, GET /api/broadcasts/metrics-bulk covers a whole dashboard in
one request, and a contact export reads out an
entire audience for the price of a handful of polls.
A few endpoints — login, password reset, and other unauthenticated surfaces — carry their own stricter limits that apply regardless of credentials.
Scheduling errors#
NO_SEGMENT#
Meaning:
- the broadcast does not have an included audience
NO_MTA#
Meaning:
- the broadcast does not have a valid MTA configuration
NO_IDENTITY#
Meaning:
- the broadcast cannot resolve a valid sender identity or was given an invalid empty sender list
NOT_DRAFT#
Meaning:
- the requested action only applies to drafts
Troubleshooting checklist#
If a broadcast request fails:
- Confirm the tenant slug in the URL is correct.
- Confirm the API key has the expected scopes.
- Confirm
email_type_keyexists inGET /api/email-types. - Confirm
mta_config_keymatches a sending provider configured in the app, or omit it and let SonarSend resolve the delivery path. - Confirm
include_segment_keys/exclude_segment_keysexist inGET /api/segments. - Confirm
sending_identity_emailsexist inGET /api/sending-identities. - If using template-mode, confirm
content.template_slugexists inGET /api/templates. - If scheduling fails, confirm the draft is complete enough to send.