Send a message

Search Documentation

Search across all developer documentation

send

Send a message

POST https://ingest.sendops.dev/v1/send
Auth required api.send

Idempotency, limits and retries are covered once, in the Send API guide.

Sends one message through the organization's AWS SES account.

Two body formats. Structured JSON (Content-Type: application/json) is the documented default: SendOps builds the MIME message from from, to, subject, html/text, attachments and headers, or renders a stored template with variables. Raw MIME (Content-Type: message/rfc822) is also accepted for senders that already build their own messages, such as an SMTP relay or a migration from SendRawEmail; the body is sent as-is, with envelope recipients taken from its To, Cc and Bcc headers. JSON is the default because it is what lets SendOps validate the request, render templates and report a precise 400; raw MIME exists so that nothing a customer sends today through SES is impossible here.

A body that is neither application/json nor message/rfc822 is 400 validation_failed with field: Content-Type. A structured body is decoded strictly: an unknown member is a 400 naming it.

200 means SES accepted the message and returns its message_id. 202 is answered by the edge only, never by the origin: the request is stored and will be sent when the origin recovers. See the API description for idempotency, size and retry rules.

Headers

Idempotency-Key string optional

A client-chosen key, unique per message (a UUID is ideal). Reuse it when retrying the same message: a repeat with the same body for at least 7 days replays the original response instead of sending again. The same key with a different body is 409 idempotency_key_conflict. If absent, one is generated for the request. At most 255 characters (400 validation_failed on Idempotency-Key otherwise).

Request body

Content type: application/json

from string required

Sender, as address or Name <address>. The address must be on a domain, or be an email identity, verified in the organization's SES account and managed by SendOps for the organization.

to array<string> required

Recipients, each as address or Name <address>.

Constraints: 1–50 items

subject string optional

Required with html/text. With template it is optional and is passed to the template as its subject variable.

html string optional

HTML body.

text string optional

Plain-text body.

attachments array<object> optional

Each entry in attachments:

filename string required
content string required

The file's bytes, base64-encoded. Counts toward the 40 MB request cap at its encoded size.

content_type string optional

MIME type. Inferred from filename when absent.

headers object optional

Extra message headers, name to value. Headers SendOps sets itself (From, To, Subject, Date, Message-ID, MIME-Version, Content-Type) cannot be overridden. A Reply-To header is accepted and treated as reply_to; set one or the other.

reply_to string | array<string> optional

Where replies go, as one address or Name <address>, or an array of them (at most 50). Works with html/text and with template.

template string optional

Id or name of a stored template to render, which must be deployed to the organization's SES account. Excludes html, text and attachments.

variables object optional

Values substituted into template. Ignored without one.

Responses

Errors follow the RFC 7807 problem format — see the error reference.

200 Sent. SES accepted the message. application/json
status string required
message_id string required

The SES message id.

request_id string required

Resolves through GET /v1/messages/{request_id}.

202 Queued by the edge, because the origin was unavailable or shedding load. The request is stored durably and will be sent on recovery. Poll GET /v1/messages/{request_id} for the outcome. Never returned by the origin. application/json
status string required
request_id string required

Resolves through GET /v1/messages/{request_id}.

400 The request is malformed or fails validation (code: validation_failed). Terminal: fix the request before retrying. The edge also answers it, with X-SendOps-Path: rejected, when the body was cut off mid-upload; resend the whole request. application/problem+json
401 Missing, malformed, unknown or revoked API key, or a non-API-key credential such as an OAuth token (code: invalid_key); or a rotated key past its 24-hour grace window (code: key_expired). Carries a WWW-Authenticate: Bearer challenge. The edge answers the same codes itself, with X-SendOps-Path: fallback, when it checks the key before queueing a send. A revoked key is invalid_key; key_revoked is never an HTTP response, only a lookup's error.code. application/problem+json
403 The key lacks the api.send scope (code: invalid_scope), or it is an sk_test_ key and a recipient is neither an SES mailbox-simulator address nor on one of the organization's own verified, managed identities (code: test_environment_forbidden), or the request did not come through the edge at ingest.sendops.dev (code: edge_required). Nothing was sent. The edge answers invalid_scope itself, with X-SendOps-Path: fallback, when it checks the key before queueing. application/problem+json
409 The Idempotency-Key was already used for a different request body, or a request under the same key is still in flight (code: idempotency_key_conflict). Use a new key for a new message. The in-flight case carries Retry-After: retry with the same key after it to get the outcome. application/problem+json
413 The request body exceeds 40 MB (code: payload_too_large). application/problem+json
429 Rate limit exceeded (code: rate_limited), or the organization has used its plan's monthly fair-use number of API sends (code: send_quota_exceeded, with limit, used and resets_at). Nothing was sent. Retry after Retry-After; for send_quota_exceeded that is the moment the billing period resets. While the edge is queueing sends, it also limits each key to 100 requests per 10 seconds and answers rate_limited itself, with Retry-After: 10 and X-SendOps-Path: fallback. application/problem+json
500 Unexpected server error (code: internal_error). Quote request_id to support. application/problem+json
503 Temporarily unable to accept the request. code: overloaded with X-SendOps-Shed: 1 when SendOps is shedding load; code: upstream_unavailable when the organization's AWS SES throttled, is out of daily quota, could not be reached, or failed without confirming the send (never with X-SendOps-Shed). The edge also answers upstream_unavailable with Retry-After: 2 and X-SendOps-Path: fallback when it cannot reach the SendOps origin, the origin times out, or recent failures have it skipping the origin for a few seconds; and with X-SendOps-Path: direct plus X-SendOps-Reason: unreplayable when the origin failed after a body over 10 MB was already streamed to it. Both codes are retryable after Retry-After, with the same Idempotency-Key. A definite SES refusal of the message (MessageRejected, sending paused, unverified MAIL FROM) is a 400 validation_failed with SES's reason instead. application/problem+json