SendMailOS
Guides

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.

StatusMeaningTypical next step
400Invalid input, content, domain verification, or scheduleCorrect the request.
401Missing or invalid Bearer tokenSupply a valid credential.
402payment_required: a Stripe subscription payment failed and sending is pausedUpdate the payment method and pay the outstanding invoice in the dashboard. Existing idempotent replays still return their original result.
403Scope, plan, key restriction, inactive organization, paused delivery, or insufficient creditsRead error.message and fix the stated condition.
404Resource or endpoint not foundVerify the path and organization.
405HTTP method not supported for the endpointUse a documented method.
409State conflict or idempotency conflictInspect the current resource or retry the original request as appropriate.
413Email request exceeds the endpoint body limit (payload_too_large)Reduce the payload; the error includes limit_bytes.
422Recipient suppressed or broadcast audience emptyReview the recipient or audience.
429Request rate or quota exceededFollow Retry-After when present. A monthly quota has no short retry window.
503Worker or provider unavailableRetry with backoff when retryable is true.
500Unexpected server failureRecord 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.

On this page