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
| Field | Type | Notes |
|---|---|---|
from (required) | string | A verified sender on one of your authenticated domains. |
to | string[] | Recipients. At least one of to/cc/bcc is required; 50 max per call. |
cc, bcc | string[] | Optional additional addresses. |
reply_to | string | Optional reply-to address. |
subject | string | Required unless a template supplies it (max 1,000 chars). |
html_body, text_body | string | At least one required unless a template supplies the body. |
message_stream | string | outbound (default) or broadcast — keeps transactional and promotional reputation apart. |
template_alias, template_model | string / object | Render a server-side template; see Templates. |
tags | object | Arbitrary string key/values for filtering activity and analytics, e.g. { "campaign": "welcome" }. (tag is the legacy single-string form.) |
scheduled_at | integer | Epoch milliseconds in the future — the message is held and sent at that time. |
attachments | object[] | Up to 10. Each needs filename + content_type, with either base64 content or an r2_key from POST /v1/attachments. |
test_mode | boolean | Simulate delivery without sending (same as the X-Relay-Test: true header). |
Request and response
{
"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" }
}{
"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:
{
"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 newscheduled_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.