Send an email
Queues a send to one recipient from a published template. Returns 202 with a message id once the send is durably recorded — never waiting on the provider, so this is safe to call from inside your own user-facing request. Address verification, rendering and provider dispatch happen in the background, exactly as they do behind SendGrid's 202 Accepted and Resend's immediate id.
Problems with the REQUEST still come back right away as a 4xx: an unknown or unpublished template, no verified sender, a suppressed or undeliverable recipient, a paused project, a cap. What moves is the delivery itself.
Follow a message with GET /api/v1/emails/{id}, or subscribe to your provider's webhooks. Pass an Idempotency-Key header to make retries safe.
Requires a write-scope key.
bearerAuth writeAuthorizationBearer <token>An API key from the dashboard under Settings → API keys, sent as Authorization: Bearer aem_….
Authorization has two independent axes.
The scope is ranked — a key satisfies any requirement at or below its own tier:
read— see messages, contacts and metrics. Changes nothing, and cannot send.write— everythingreaddoes, plus managing templates, contacts, automations, segments and suppressions. This is what editing a template needs.admin— everythingwritedoes, plus sending configuration: domains, senders, webhook registration, kill switch, daily cap, brand.
There is no approve scope. It was a rung once; it is not one now, and a key requested with it is rejected.
The approval grant (can_approve) is a separate boolean, not a rung. Delivering mail to a real inbox needs write and the grant. Keeping them on separate axes is what makes the review gate a control rather than a convention: a key that may propose is not automatically a key that may approve its own proposal.
Give your application the lowest tier that works. Most need write and the grant — the dashboard mints that combination as Send + manage; Manage only is the same rung with the grant withheld.
Scope: write
Idempotency-Key?stringUnique per logical send (≤256 chars). A repeat returns the original result with deduplicated: true.
length <= 256application/json- body
to*stringRecipient email address. Single recipient — there are no bulk sends.
^(?:[A-Za-z0-9_'+\-]+\.)*[A-Za-z0-9_'+\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$emailcc?|array<>Carbon-copy recipient(s) of the same message — a string or array. Not a way to bulk-send.
bcc?|array<>Blind-carbon-copy recipient(s) — a string or array.
template*stringSlug of a published template in this project.
1 <= lengthprops?Values for the template's variables.
locale?stringDeliver a published translation for this locale (e.g. fr, pt-BR); falls back to the base language when no translation exists.
2 <= length <= 35from?stringSender, as Name <addr@domain> or a bare address. The address must be on a verified sending domain in this project. Omit to use the project's default sender.
3 <= length <= 320reply_to?|array<>Reply-To address(es) — a string or array.
headers?Custom email headers. Reserved headers (List-Unsubscribe, Idempotency-Key, Cc/Bcc/Reply-To) are rejected.
tags?array<>Up to 10 {name,value} metadata tags, forwarded to the provider for analytics.
items <= 10scheduled_at?stringISO 8601 timestamp, or natural language like in 2 hours / tomorrow 9am. A future value schedules the send via the worker. Cannot be combined with attachments. Manage with PATCH (reschedule) / DELETE (cancel).
1 <= lengthattachments?array<>Up to 10 attachments (base64 content). Total ≤ ~5 MB. Cannot be combined with scheduled_at.
items <= 10Idempotent replay — the Idempotency-Key matched a send we already accepted. Carries that send's current status.
application/json- response
id*stringMessage id — use it with GET /api/v1/emails/{id}.
status*stringsent (delivered to provider synchronously), queued (throttled to the worker), or scheduled.
"sent""queued""scheduled"deduplicated?booleanPresent and true when a matching Idempotency-Key replayed a prior send.
curl -X POST 'https://sendandretain.com/api/v1/emails' \ -H 'Idempotency-Key: welcome-jane-2026-07-20' \ -H 'Content-Type: application/json' \ -d '{ "to": "[email protected]", "template": "welcome", "props": { "firstName": "Jane" }, "from": "Acme <[email protected]>"}'{ "id": "msg_3aF9c1", "status": "sent", "deduplicated": true}