Docs
API reference Getting started Errors & troubleshooting

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_key was not found for the tenant

INVALID_MTA_CONFIG#

Meaning:

  • mta_config_key was 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_slug was not found for the tenant

INVALID_METADATA#

Meaning:

  • broadcast metadata fails 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 metadata query parameter on the list endpoint is not valid JSON or not a plain object

INVALID_STATUS#

Meaning:

  • the status query 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_html and content.body_mjml (unless using template-mode with template_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 positive weight
  • 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:

HeaderMeaning
X-RateLimit-LimitRequests allowed in the window
X-RateLimit-RemainingRequests left
X-RateLimit-ResetSeconds 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:

  1. Confirm the tenant slug in the URL is correct.
  2. Confirm the API key has the expected scopes.
  3. Confirm email_type_key exists in GET /api/email-types.
  4. Confirm mta_config_key matches a sending service configured in the app, or omit it and let RadarSend resolve the delivery path.
  5. Confirm include_segment_keys / exclude_segment_keys exist in GET /api/segments.
  6. Confirm sending_identity_emails exist in GET /api/sending-identities.
  7. If using template-mode, confirm content.template_slug exists in GET /api/templates.
  8. If scheduling fails, confirm the draft is complete enough to send.