Carillon docs

API errors

Error response format, codes, and corrective actions.

Every failure is RFC 9457 Problem Details, with Content-Type: application/problem+json. Validation failures, unknown routes and unhandled exceptions all use the same shape.

{
  "type": "https://carillon.dev/errors/payload_too_large",
  "title": "Payload exceeds the provider limit",
  "status": 422,
  "code": "payload_too_large",
  "docs": "https://app.carillon.dev/docs/errors#payload_too_large",
  "detail": "The rendered payload is 4312 bytes; APNs allows 4096.",
  "actual_bytes": 4312,
  "limit_bytes": 4096
}
FieldUse
codeSwitch on this. It is stable, and every value appears below
titleNames the class of problem
detailDescribes this occurrence, and says what to do next
ExtensionsThe numbers you need to act, such as actual_bytes against limit_bytes

Sending

empty_payload

422. The notification would show nothing.

Send at least a title, a body or a raw apns override. A notification with none of the three arrives on the handset showing nothing at all.

payload_too_large

422. The rendered payload exceeds the provider limit, which is 4096 bytes for APNs.

Shorten the title, the body or your custom data. Where localizations are involved, the heaviest translation is the one that counts, not the base text.

reserved_data_key

422. The payload claims the carillon data key.

Rename your key. Every payload carries a carillon key holding the delivery identifier that open tracking needs, and a payload that sets it itself is refused rather than silently overwritten.

unknown_devices

422. Some devices are not registered in this app.

Register these devices before sending to them. An id belonging to another app is unknown here.

unknown_audience

422. No such saved audience in this app.

List your saved audiences and send the id of the one you meant, or send device_ids instead. An audience belonging to another app is unknown here.

no_valid_credential

422. No provider will accept this app's credential, and this campaign could not produce a single notification.

The detail names which credential, when the provider last refused it, and what to do. A campaign with one working platform and one broken one is accepted instead, and the broken half arrives as a warnings entry on the 202.

delivery_rate_too_high

422. The rate asked for is above your app's ceiling.

Lower it, or omit it to use the ceiling. A rate above the ceiling is refused rather than capped.

campaign_not_paused

409. Resume was asked for on a campaign that is not paused.

Scheduling

send_at_out_of_range

422. The instant is in the past, or more than 30 days ahead.

Send an instant in the future, or omit send_at to send now. An instant in the past is refused rather than sent immediately. For a later send, create the campaign when it is within the 30-day window.

missing_timezone_fallback

422. A local_time campaign carries no timezone_fallback.

Add one, an IANA zone such as Europe/Paris. A device that never reported its timezone has no local 10:00, and the fallback is the wave it goes in.

unknown_timezone

422. That is not a timezone the tz database knows.

Send an identifier such as Europe/Paris, never an offset like +02:00. An offset does not carry the daylight saving transitions.

Idempotency and deduplication

idempotency_key_reused

422. This Idempotency-Key was used with a different body.

Use a new key for this request, or send the body the key was first used with. The original answer is not returned for a different body: it names identifiers belonging to another notification.

idempotency_key_in_flight

409, with Retry-After. An earlier request under this key is still being processed.

Retry after the delay.

duplicate_dedup_key

409, with the campaign_id that holds the key.

Read that campaign to see what became of it, or send this under a different dedup_key if it is a different notification. Deduplication records are retained for 30 days. Idempotency-Key replays the original response during its 24-hour retention window.

Devices and imports

empty_import

422. The body is empty.

Send the CSV, starting with a header row that names at least token and platform.

request_too_large

413. The request body exceeds the route's limit: 1 MiB on POST /v1/messages, 64 KiB on POST /v1/devices. The limit_bytes extension carries the ceiling.

Send fewer external_ids per request, or a smaller payload. Two thousand identifiers of 256 characters fit under the limit with room for the payload.

import_too_large

413. The file is larger than one request may carry.

Split it and import the parts in any order: a token already imported is skipped, so overlapping files are safe.

unsupported_media_type

415. The body is not a CSV.

Send the CSV as the request body with Content-Type: text/csv. This endpoint takes the file itself, not a JSON envelope around it.

Audiences

duplicate_audience_name

409. An audience of this app already goes by that name.

Give this one a different name, or delete the one holding it.

Authentication and authorization

unauthorized

401. No valid API key was supplied.

Send your key as Authorization: Bearer carillon_sk_live_…. Secret keys are shown once when created; a lost one is replaced, never recovered.

On the management section the message differs: send your session token as Authorization: Bearer …. Sign in on the dashboard to obtain one, or run carillon login. API keys do not open the management section.

forbidden

403. This key may not perform that action.

A mobile key cannot send, list or read a trace. It ships inside your mobile app and can be read by anyone who has the binary. Use a secret key from your backend.

not_found

404. No such resource, in this app.

A resource that belongs to another app answers the same way as one that does not exist.

rate_limited

429. Too many requests.

Wait the number of seconds the detail names, then retry. See rate limits.

Invitations

invitation_expired

410. Ask whoever invited you to send a new one; it takes them one click.

invitation_not_pending

409. That invitation has already been used or withdrawn. Sign in if you have an account, or ask for a new invitation.

account_already_exists

409. That address already has an account. Sign in with it, then open the link again to join.

Errors on our side

internal_error

500. Something went wrong on our side.

A thrown message is never returned. It is logged and replaced by a generic body, since exception messages can quote the input. Retry, and send us the request if it persists.

mail_not_sent

502. The invitation was created but the email did not go out. Send it again from the pending list.

mail_not_configured

503. This environment cannot send email until a verified sender domain is configured for it.

invalid_request

400. The request could not be understood. The detail names the field.

Rate limits · Troubleshooting · API reference

On this page