Skip to main content
After sending a message with the Courier API, you’ll receive a response indicating whether the request was successful and how to track its delivery. Understanding these responses and handling errors correctly ensures reliable delivery and better visibility into message outcomes. This guide explains how to:
  • Interpret success and error responses from the Send API
  • Implement robust error-handling logic in production
  • Monitor message status in the Courier dashboard
  • Receive real-time delivery updates through webhooks
By following these patterns, you can build resilient notification workflows that recover from temporary issues and provide clear delivery insight to your team.

Response Handling

Success Response

Successful sends return a request ID for tracking:
{
  "requestId": "87e7c05b-4f46-fda24e356e23"
}

Error Responses

If a request fails, the Courier API returns an error response with an HTTP status code, a message describing the issue, and a type classifying the error category:
{
  "message": "Invalid Request. Either 'content' or 'template' must be defined.",
  "type": "invalid_request_error"
}
Error types map to status code ranges:
TypeStatus CodesMeaning
invalid_request_error400, 404, 409, 413The request is malformed or references a resource that doesn’t exist. Fix the request before retrying.
authentication_error401The API key is missing, invalid, or expired.
authorization_error403The API key is valid but doesn’t have permission for this operation.
rate_limit_error429You’ve exceeded a rate limit. Back off and retry.
api_error500, 503Something went wrong on Courier’s side. Retry with exponential backoff.

HTTP Status Code Reference

CodeNameDescriptionAction
400Bad RequestInvalid payload; missing required fields, wrong types, or conflicting properties. The message field explains exactly what’s wrong.Fix the request body. See Common Validation Errors below.
401UnauthorizedMissing or invalid Authorization header.Check that you’re sending Bearer <API_KEY> and that the key hasn’t been revoked. Make sure you’re using the correct environment key (test vs. production).
403ForbiddenThe API key doesn’t have access to this resource or workspace.Verify the key’s scope and workspace permissions.
404Not FoundThe resource (template, list, user, etc.) doesn’t exist.Confirm the ID is correct and belongs to the same workspace/environment as your API key.
409ConflictA resource with that ID already exists, or a concurrent modification conflict occurred.Use the appropriate update endpoint, or retry with an idempotency key.
413Payload Too LargeThe request body exceeds the 6 MB limit.Reduce payload size. For attachments, host files externally and pass download URLs via data variables. Base64 encoding inflates size by ~33%. See Payload Limits.
429Too Many RequestsRate limit exceeded.Back off and retry. Check the X-RateLimit-Remaining header to pace requests. See Rate Limits below.
500Internal Server ErrorAn unexpected error on Courier’s side.Retry with exponential backoff. If persistent, contact support.
503Service UnavailableCourier is temporarily overloaded or undergoing maintenance.Retry with exponential backoff.

Common Validation Errors

These are the most frequent 400 errors on the Send endpoint and how to fix them:
Error MessageCauseFix
Either 'content' or 'template' must be defined.The message object has neither a content block nor a template reference.Add a content object with title/body/elements, or set template to a valid notification ID or slug.
Either 'content' or 'template' may be defined, but not both.Both content and template are present.Remove one; use content for inline messages or template for designer-built notifications.
The 'to' property is required.No recipient specified.Add a to object with user_id, email, list_id, audience_id, or ad-hoc routing.
The 'to' property must be of type object or array.to is a string or other primitive.Wrap the recipient in an object: { "user_id": "..." }.
The list id '...' was not found.The referenced list doesn’t exist.Create the list first via the Lists API, or check for typos.
The audience_id '...' was not found.The referenced audience doesn’t exist.Create the audience first via the Audiences API.
'content' must contain one of: 'title', 'body', or 'elements'The content object is empty or has only unrecognized keys.Add at least a title or body field.

Rate Limits

Courier rate-limits certain management endpoints to protect platform stability. Rate-limited responses include X-RateLimit-Limit and X-RateLimit-Remaining headers so you can pace requests proactively.
EndpointOperationLimit
Lists APIPOST to subscriptions20 requests/minute
Lists APIPUT to lists20 requests/minute
Events APIPUT to events20 requests/minute
Brands APIPUT to brands200 requests/minute
The Send endpoint (POST /send) is not rate-limited by request count. For volume-based message throttling (per-user, per-topic, per-tenant), see Send Limits. When you receive a 429, wait before retrying. A simple strategy:
  1. Read X-RateLimit-Remaining on every response to detect approaching limits
  2. On 429, wait 60 seconds (the rate limit window) before retrying
  3. For bursts, implement exponential backoff starting at 1 second

Monitoring & Debugging

Message Logs

Message Logs in the Courier dashboard provide real-time visibility into every message your system sends. Each log entry shows the message’s delivery path, status, and any associated errors. This helps you confirm successful sends and troubleshoot failed or delayed deliveries. Common message statuses include:
  • SENT: The message was successfully handed off to the provider.
  • DELIVERED: The provider confirmed successful delivery to the recipient.
  • FAILED: Delivery was not completed. Review error details to identify the cause.
  • THROTTLED: The message was rate limited or blocked by configured guardrails.
Message Status Details: For a full list of message statuses and their definitions, see the Message Logs documentation.

Delivery Status Updates via Webhooks

Use webhooks to receive real-time updates when a message’s delivery status changes. Courier sends a message:updated event to your configured webhook endpoint each time a message is sent, delivered, failed, or retried. Webhook events let your application track delivery progress without polling the API. You can use them to update user interfaces, trigger automations, or store delivery data in your own systems.
{
  "type": "message:updated",
  "data": {
    "id": "1-6143cf63-4f27670f6304f465462695f2",
    "status": "DELIVERED",
    "recipient": "c156665c-a76c-4440-9676-f25c1b04ba93",
    "timestamp": "2024-01-15T10:30:00Z"
  }
}
Webhook Setup: To receive these notifications, you need to configure outbound webhooks in your Courier workspace. Go to Settings → Outbound Webhooks and add your webhook endpoint URL.Implementation: Your webhook endpoint should return a 200 response quickly and handle the event data asynchronously. For complete webhook setup instructions, including signature verification and event handling, see Outbound Webhooks.

Best Practices

Error Handling

Implement proper error handling for production use:
try {
  const { requestId } = await courier.send(messageData);
  console.log(`Message sent successfully: ${requestId}`);
} catch (error) {
  if (error.statusCode === 429) {
    // Rate limited - implement backoff
    console.log("Rate limited, retrying later...");
  } else if (error.statusCode >= 500) {
    // Server error - retry with exponential backoff
    console.log("Server error, retrying...");
  } else {
    // Client error - check request format
    console.log("Invalid request:", error.message);
  }
}

What’s Next

Message Logs

Track delivery status and debug failed messages

Outbound Webhooks

Receive real-time delivery status updates