Guides

Send an email

POST /v1/email — body parameters, templates, scheduling, attachments, and the response shape.

Queue a message for delivery with POST /v1/email. emitd validates it, checks the suppression list, rate-limits per tenant, and hands it to SES. You get a 202 back immediately with a message_id.

curl https://api.emitd.com/v1/email \
  -H "Authorization: Bearer $EMITD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ ... }'

Full request/response schemas: Send an email.

Body parameters

FieldTypeNotes
from (required)stringA verified sender on one of your authenticated domains.
tostring[]Recipients. At least one of to/cc/bcc is required; 50 max per call.
cc, bccstring[]Optional additional addresses.
reply_tostringOptional reply-to address.
subjectstringRequired unless a template supplies it (max 1,000 chars).
html_body, text_bodystringAt least one required unless a template supplies the body.
message_streamstringoutbound (default) or broadcast — keeps transactional and promotional reputation apart.
template_alias, template_modelstring / objectRender a server-side template; see Templates.
tagsobjectArbitrary string key/values for filtering activity and analytics, e.g. { "campaign": "welcome" }. (tag is the legacy single-string form.)
scheduled_atintegerEpoch milliseconds in the future — the message is held and sent at that time.
attachmentsobject[]Up to 10. Each needs filename + content_type, with either base64 content or an r2_key from POST /v1/attachments.
test_modebooleanSimulate delivery without sending (same as the X-Relay-Test: true header).

Request and response

POST /v1/email
{
  "from": "you@yourdomain.com",
  "to": ["ada@lovelace.io"],
  "reply_to": "support@yourdomain.com",
  "subject": "Your receipt #4021",
  "html_body": "<h1>Thanks!</h1>",
  "text_body": "Thanks!",
  "message_stream": "outbound",
  "tags": { "campaign": "receipts" }
}
202 Accepted
{
  "message_id": "msg_2h8Kd0Rk9Qa",
  "status": "queued"
}

The status field is one of queued, suppressed, scheduled, sent, or failed — 202 + queued is the happy path for an immediate send. A suppressed recipient instead returns 200 with { "status": "suppressed" } and nothing is sent.

Sending with a template

Reference a template by alias and pass a model — emitd renders the active version server-side before sending:

POST /v1/email
{
  "from": "you@yourdomain.com",
  "to": ["ada@lovelace.io"],
  "template_alias": "receipt",
  "template_model": { "name": "Ada", "total": "$48.00" }
}

Scheduling

Pass scheduled_at (epoch milliseconds, in the future) and the message is persisted as scheduled, then enqueued by a per-minute cron once due. While it's still scheduled you can:

  • reschedule: PATCH /v1/email/{id} with a new scheduled_at
  • cancel: DELETE /v1/email/{id}

Attempts to send a scheduled message that's already out the door fail with message_not_cancellable / message_not_reschedulable.

Attachments

Inline (up to 10 MB each):

"attachments": [
  { "filename": "invoice.pdf", "content_type": "application/pdf", "content": "JVBERi0xLjQK..." }
]

Or upload once with POST /v1/attachments (multipart) and reference the returned key:

"attachments": [
  { "filename": "invoice.pdf", "content_type": "application/pdf", "r2_key": "att/9f3..." }
]

Safe retries

Pass an Idempotency-Key header on any send. If the request is retried — a timeout, a crashed worker, a network blip — the same key returns the original result instead of sending twice.

Batch

For 2–100 messages in one call, use POST /v1/email/batch.

On this page