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)
MJML_UNKNOWN_ELEMENT#
Meaning:
- the MJML uses an element the compiler doesn’t recognize. The compiler would drop that element and everything inside it, so the email would send without that content. The message names the element and its line.
Returned when saving a template, a content block, a broadcast (raw MJML, template mode, or a variant override) or a message library version.
Typical causes:
- MJML 3’s
<mj-container>: delete its opening and closing tags and keep the sections inside them directly in<mj-body> - a misspelled tag, such as
<mj-txt> - plain HTML (
<p>,<table>) directly inside<mj-column>or<mj-section>: wrap it in<mj-text>or<mj-raw>
Other MJML warnings, such as a {{ … }} placeholder in an attribute that
expects a color or a size, don’t block a save.
MJML_COMPILE_FAILED#
Meaning:
- the broadcast’s MJML compiled to no HTML at all
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 service configured in the app, or omit it and let RadarSend 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.