Send a transactional email
Call the send endpoint, schedule sends, and attach files.
Transactional email — receipts, resets, confirmations — is a single
POST /api/v1/emails. These always deliver, even to unsubscribed contacts,
because they're not marketing (unsubscribe blocks only lifecycle templates).
Send now
curl https://sendandretain.com/api/v1/emails \
-H "Authorization: Bearer $AEM_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: receipt-ord-991" \
-d '{
"to": "[email protected]",
"template": "receipt",
"props": { "orderId": "ord_991", "total": "42.50" },
"from": "Acme <[email protected]>"
}'const res = await fetch("https://sendandretain.com/api/v1/emails", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AEM_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "receipt-ord-991",
},
body: JSON.stringify({
to: "[email protected]",
template: "receipt",
props: { orderId: "ord_991", total: "42.50" },
from: "Acme <[email protected]>",
}),
});
console.log(await res.json());import os, requests
res = requests.post(
"https://sendandretain.com/api/v1/emails",
headers={
"Authorization": f"Bearer {os.environ['AEM_KEY']}",
"Idempotency-Key": "receipt-ord-991",
},
json={
"to": "[email protected]",
"template": "receipt",
"props": {"orderId": "ord_991", "total": "42.50"},
"from": "Acme <[email protected]>",
},
)
print(res.json())from is optional — omit it to use the project's default sender. It must resolve
to a verified domain.
Schedule for later
Pass an ISO 8601 scheduled_at in the future; the worker sends it at that time.
Scheduling is handled by Send & Retain (not the provider), so it's uniform across
providers and cancellable.
{ "to": "[email protected]", "template": "reminder", "scheduled_at": "2026-08-01T09:00:00Z" }Attachments can't be combined with scheduled_at — attachment content is never
persisted, so scheduled sends can't carry it.
Attach files
Attachments are base64-encoded, up to 10 per message and ~5 MB total:
{
"to": "[email protected]",
"template": "invoice",
"attachments": [
{ "filename": "invoice.pdf", "content": "JVBERi0xLjQK...", "content_type": "application/pdf" }
]
}Idempotency
Always pass an Idempotency-Key for sends you might retry — see
Rate limits & idempotency. A repeat returns the original
result with deduplicated: true.