Responses and errors
Interpret HTTP status, accepted sends, warnings, and retryable API errors.
An accepted send is not a delivery receipt
POST /v1/emails returns 202 Accepted after accepting the request. The
response includes request_id, message, id, status, mode, and warnings.
For a normal live send, the message is:
Message accepted and queued for delivery. Acceptance does not confirm delivery.
Check GET /v1/emails/{id} or subscribe to delivery webhooks for later
outcomes. A scheduled email returns status: "scheduled". A suppressed email
can also return 202, with status: "suppressed" and a warning; it will not
be delivered. Test-mode sends are simulated and include a warning that no real
mailbox will receive them. Inspect both status and warnings before showing
success to your user.
HTTP status and the error envelope
Successful resource calls generally return 200 OK for reads and completed
updates, 201 Created for new resources, and 202 Accepted for asynchronous
email or broadcast sends. Read the endpoint's response schema: success fields
vary by resource, and a 202 may still include warnings or a suppressed
status.
Most /v1 resource failures use this structure:
{
"request_id": "req_...",
"error": {
"code": "invalid_request",
"message": "Invalid JSON request body.",
"retryable": false
}
}Some errors add error.details with structured context. Keep request_id in
your logs when asking support to investigate. Display the human-readable
message; use code and HTTP status for program logic.
| Status | Meaning | Typical next step |
|---|---|---|
400 | Invalid input, content, domain verification, or schedule | Correct the request. |
401 | Missing or invalid Bearer token | Supply a valid credential. |
402 | payment_required: a Stripe subscription payment failed and sending is paused | Update the payment method and pay the outstanding invoice in the dashboard. Existing idempotent replays still return their original result. |
403 | Scope, plan, key restriction, inactive organization, paused delivery, or insufficient credits | Read error.message and fix the stated condition. |
404 | Resource or endpoint not found | Verify the path and organization. |
405 | HTTP method not supported for the endpoint | Use a documented method. |
409 | State conflict or idempotency conflict | Inspect the current resource or retry the original request as appropriate. |
413 | Email request exceeds the endpoint body limit (payload_too_large) | Reduce the payload; the error includes limit_bytes. |
422 | Recipient suppressed or broadcast audience empty | Review the recipient or audience. |
429 | Request rate or quota exceeded | Follow Retry-After when present. A monthly quota has no short retry window. |
503 | Worker or provider unavailable | Retry with backoff when retryable is true. |
500 | Unexpected server failure | Record request_id; retry with backoff only when safe for the operation. |
retryable indicates whether the condition may resolve on a later attempt.
For email sends, reuse the same Idempotency-Key when retrying within its
24-hour retention window. Do not assume that retrying a different write is
protected against duplicates.
After a failed Stripe subscription payment, new API and SMTP sends are rejected before
credits are reserved. SMTP returns a temporary 451 response so the client can
retry after billing is fixed. Mail already accepted into the queue is held
before SES or a connected server; it can resume automatically if payment is
confirmed before its queue age limit. Entries older than 24 hours expire
instead of sending stale mail. Scheduled campaigns can expire after 72 hours.
Overdue workflow steps can resume after payment; they are not subject to the
transactional queue's 24-hour expiry until their email has been queued.
Check message and campaign status before retrying, and use the same
Idempotency-Key for a retry of the original API request.
Authenticated responses include X-RateLimit-Limit,
X-RateLimit-Remaining, and X-RateLimit-Reset. Request-rate and daily
API-request limit responses include Retry-After; a monthly credit limit does
not have a fixed retry delay.
Single email requests allow up to 36 MiB of JSON so base64 attachments fit.
The rendered HTML and text together may be up to 2 MiB. Attachments may be up
to 10 MiB each and 25 MiB combined after decoding. Batch requests have a
10 MiB JSON body limit. Invalid base64 and unsafe attachment headers return
400 invalid_request before the message is queued.
The OAuth token exchange at POST /oauth/token follows OAuth conventions:
failures use { "error": "invalid_grant", "error_description": "..." },
not the /v1 error envelope. Public health endpoints also have their own
response shapes.