Skip to main content
A send is asynchronous. The API returns a requestId, and the message moves through a status lifecycle you track in the logs.

How it works

The status lifecycle

A message’s status only advances, never backward. Two independent systems drive it. Provider confirmation produces DELIVERED. Courier tracking produces OPENED and CLICKED, so engagement can arrive before or without a delivery confirmation. Open a message in the to see its provider response.

Send API responses

A successful send returns a requestId you use to track the message:
A failed request returns an HTTP error with a message and a type: The most common 400 on /send is Either 'content' or 'template' must be defined. Send one, not both. Next most common are a missing to, or a list_id/audience_id that does not exist. A 413 means the request exceeded the 6 MB payload limit. Host attachments externally and pass URLs in data rather than base64.

Limits

is not rate-limited by request count, so you can send at volume. Its request body caps at 6 MB, as does . Every other endpoint caps at 1 MB. A few management endpoints are rate-limited per workspace. Their responses carry X-RateLimit-Limit and X-RateLimit-Remaining so you can pace requests. Exceeding a limit returns 429 Too Many Requests: Endpoints not listed here are not request-count limited today.

Send limits

Send limits cap how many messages a user, topic, or tenant receives in a window. They are a separate feature from API rate limits. A message blocked by one shows as THROTTLED in the log instead of delivering. The Developer plan’s 10,000-sends-per-period cap produces the same THROTTLED status. That drop happens after the API has already returned 202, so there is no synchronous error and the requestId looks healthy. A THROTTLED message you did not configure a limit for usually means the plan cap.

Why a message is UNROUTABLE

UNROUTABLE is one status with several causes. Open the message in and read its reason: The timeline then shows one attempt per provider, each with its own reason for being skipped: To audit in bulk, list the failures over the API:

Debug other statuses

  • Stuck at SENT: delivery tracking is not configured for that channel, not a failure. See delivery tracking.
  • FILTERED: preferences, send conditions, or routing removed the message. Check the recipient’s .
  • UNDELIVERABLE: the provider rejected it. The timeline shows the provider’s error.
For real-time status without polling, receive the message:updated .

Delivery tracking is per provider

DELIVERED only appears when the provider tells Courier the message landed. That reporting is off until you set it up, per provider. Until then a healthy send stops at SENT. This is the most common reason a working integration looks broken. A provider reports in one of four ways: Providers absent from this table send no delivery confirmation, so their messages stop at SENT by design. Where both a webhook and polling are offered, prefer the webhook. It is real time and needs no extra provider permissions.

Limits & behavior

  • Status is monotonic. A message never moves backward. Engagement can still follow a terminal delivery status.
  • /send is not count-rate-limited. A few management endpoints are. Send limits govern message volume and produce THROTTLED.
  • The /send payload cap is 6 MB. Other endpoints cap at 1 MB. Pass attachment URLs in data instead of inlining large files.

FAQ

Courier found no valid route: the recipient had no address for the routed channel, or no channel was configured for the send. Check the recipient’s profile and the send’s routing.
Usually not. SENT means Courier handed it to the provider and got no delivery confirmation back. That usually means delivery tracking is not set up for that channel.
Not by request count. You can send at volume. A few management endpoints are limited (see the table above). Send limits (per user, topic, or tenant) govern message volume and mark blocked messages THROTTLED.
The request exceeded the body-size cap: 6 MB on /send and /automations/invoke, 1 MB on every other endpoint. Host attachments externally and pass their URLs in data. Base64 inflates size by about a third.