Guides

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.

Full reference

On this page