Skip to main content
Courier is one REST API: send messages, build journeys, manage templates and brands, and read delivery logs. Anything you can do in the dashboard, you can do through the API. Every endpoint is listed in the sidebar, starting with . The conventions below apply to all of them.
  • New to Courier? Send your first notification with the .
  • Building with Claude Code, Codex, or Cursor? Connect Courier over MCP or the CLI. Start with .
  • Prefer a GUI? Explore every endpoint in the .

Client libraries

Server SDKs wrap this API with typed methods, retries, and idempotency helpers. Prefer them over raw HTTP. Each page carries the install command and the current version.

Node.js

Python

Ruby

Go

Java

PHP

C# / .NET

Client SDKs embed prebuilt UI instead of calling this API directly: an and a . See for the nine client platforms.

Authentication

Pass your API key as a bearer token in the Authorization header:
Requests without a valid key return 401 Unauthorized. Keys are scoped to one , Test or Production, so a Test key never sends real notifications. Create and rotate keys in .
Your API key is a secret. Use it only in server-side code, never in a browser, mobile app, or committed source. To authenticate a client for Inbox or preferences, issue a short-lived .

Requests and responses

  • All requests and responses use JSON. Send Content-Type: application/json on requests with a body.
  • Timestamps are Unix epoch milliseconds (int64), not ISO 8601 strings.
  • Send responses return a requestId. Use it to or trace delivery in Message Logs.

Payload limits

Request bodies cap at 6 MB on all endpoints. Larger requests return 413 Payload Too Large. Base64 content, such as inline attachments, inflates size by roughly 33%. A 6 MB payload holds about 4.5 MB of raw attachment data. For anything larger, host the file and pass a URL.

Idempotency

Send an idempotency key to retry safely without sending duplicates. Use a unique value, such as a UUID, per logical request, and reuse it when you retry. Server SDKs take it as a parameter and set the Idempotency-Key header for you.
Courier caches the first response for that key, including its status code and any error, and replays it on retries. The request runs only once. A key is scoped to the endpoint it was sent to. The stored key is the value plus the request path, so the same key sent to two endpoints is two independent keys. Generate one per call you want deduplicated, not one per logical operation spanning several calls. Idempotency applies to POST only. Sending the header on any other method is accepted and does nothing, because the other methods are already idempotent by definition.

How long a response is replayed

Courier replays a response for 25 hours by default. Send x-idempotency-expiration to hold a key longer, as an ISO 8601 datetime or a Unix epoch in milliseconds, up to one year. Anything else returns 400 Expiration is not a valid date.
Three details decide how it behaves:
  • It is per endpoint, not global. The same key sent to /send and to /profiles/{user_id} creates two independent records.
  • Not every POST supports it. Supported endpoints declare Idempotency-Key among their parameters. They include , , , and .
  • Keys are retained 25 hours by default. Hold one for up to a year with x-idempotency-expiration, as described in How long a response is replayed.

Pagination

List endpoints use cursor-based pagination. Every response includes a paging object with a more boolean and a cursor:
When paging.more is true, pass paging.cursor back as the cursor query parameter to fetch the next page:
The data array field name varies by endpoint. returns results, while and return items. Check the endpoint’s own schema before you parse it.

Errors

Courier uses standard HTTP status codes. 2xx is success, 4xx is a problem with your request, and 5xx is a Courier-side error. Error responses include a JSON body with a human-readable message and a machine-readable type:

Message statuses

A 202 from means Courier accepted the request, not that the message was delivered. Track the outcome with , which returns one of these statuses: ENQUEUED, ROUTED, SENT, DELIVERED, OPENED, CLICKED, DELAYED, DIGESTED, THROTTLED, FILTERED, CANCELED, SIMULATED, and three failure states:
  • UNROUTABLE: no channel could be resolved (missing provider, or no channel data on the profile)
  • UNDELIVERABLE: the provider rejected the recipient (invalid address or blocked domain)
  • UNMAPPED: the referenced template couldn’t be found
Failed messages also carry a reason, such as BOUNCED, NO_PROVIDERS, OPT_IN_REQUIRED, or UNSUBSCRIBED.

Rate limits

is not rate-limited by request count, so you can send at volume. Instead, the you configure per user, topic, or tenant govern message volume. Three management endpoints are limited per workspace: Endpoints not listed here aren’t request-count limited today. Limited responses carry X-RateLimit-Limit and X-RateLimit-Remaining so you can pace requests. Exceeding a limit returns 429 Too Many Requests.

FAQ

Anything you can do in the Courier dashboard. Send notifications across email, SMS, push, chat, and in-app, and manage user profiles, preferences, lists, templates, brands, and tenants.
All requests go to https://api.courier.com over HTTPS.
Pass your API key as a bearer token in the Authorization header: Authorization: Bearer YOUR_COURIER_API_KEY. Keys are scoped per environment (Test or Production) and used only in server-side code.
Courier maintains official server SDKs for Node.js, Python, Ruby, Go, Java, PHP, and C# / .NET, plus client SDKs for React, React Native, iOS, Android, and Flutter. Any language can call the REST API directly.
isn’t limited by request count. Three management endpoints are limited per workspace (see the table above). Exceeding a limit returns 429 Too Many Requests.
Set an Idempotency-Key header with a unique value. Courier replays the original response for 25 hours, so a retried request runs only once.