# Send email

> Queue an email for delivery.

Source: https://sndr.sh/docs/cranl/emails/send-email

`POST https://api.sndr.sh/api/cranl/v1/send`

Queue an email for delivery. Returns 202 Accepted with the queued Email. Send an Idempotency-Key header to make retries safe: a repeat with the same key within 24 hours returns the first response.

Scope: `send`

**Body** (JSON)

- `from` (string, required): Sender, as an address or "Name <address>". Its domain must be added and verified on the connected account.
- `to` (string[], required): Recipient addresses, 1–1000, plain format (no display names). Must be an array, even for one recipient.
- `cc` (string[]): CC addresses, max 100.
- `bcc` (string[]): BCC addresses, max 100. to + cc + bcc together may not exceed 1000.
- `subject` (string): Subject line, single line, max 998 bytes. Required unless the template provides one.
- `html` (string): HTML body. Send html, text or both, unless template_id provides them.
- `text` (string): Plain-text body.
- `reply_to` (string): Reply-To address.
- `template_id` (string): Saved template ID (tpl_...). Its subject, html and text fill any of those fields you leave empty.
- `variables` (object): Values for {{ name }} placeholders in subject, html and text, e.g. {"first_name":"Alex"}. {{ user.name }} reads nested objects; unknown placeholders are left as is.
- `headers` (object): Extra headers as string pairs, e.g. {"X-Entity-Ref-ID":"1234"}. No CR/LF allowed; standard headers such as From, To, Subject, Reply-To or List-Unsubscribe are ignored.
- `tags` (object[]): Objects with string values, e.g. [{"name":"category","value":"welcome"}]. Accepted but not stored or returned yet.
- `attachments` (object[]): Up to 10 files as {filename, content, content_type?}. content is standard base64; content_type defaults to application/octet-stream. Max 10 MiB decoded in total; the whole request max 16 MiB.

**Request**

```bash
curl -X POST 'https://api.sndr.sh/api/cranl/v1/send' \
  -H 'Authorization: Bearer sndr_your_access_token' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: order-1234-confirmation' \
  -d '{
    "from":      "Acme <noreply@acme.com>",
    "to":        ["customer@example.com"],
    "reply_to":  "support@acme.com",
    "subject":   "Your order #{{ order_id }} is confirmed",
    "html":      "<h1>Thanks, {{ first_name }}!</h1><p>Order #{{ order_id }} is being prepared.</p>",
    "text":      "Thanks, {{ first_name }}! Order #{{ order_id }} is being prepared.",
    "variables": { "first_name": "Alex", "order_id": "1234" }
  }'
```

**Response** (202)

```json
{
  "id":              "em_3f9c1a2b-7d4",
  "organization_id": "org_8d41c7a2-3f6",
  "from":            "\"Acme\" <noreply@acme.com>",
  "to":              ["customer@example.com"],
  "subject":         "Your order #1234 is confirmed",
  "status":          "queued",
  "domain_id":       "dom_a7c2e910-4b1",
  "created_at":      "2026-06-01T12:00:00Z"
}
```

**Errors**

- `unauthenticated` (401): Missing or invalid Bearer token.
- `token_expired` (401): Access token expired — exchange the refresh token for a new pair.
- `token_revoked` (401): Token was explicitly revoked.
- `invalid_request` (400): Body failed validation — e.g. to sent as a string, an invalid address, no subject or body, over 1000 recipients, CR/LF in a header field, unknown template_id, or a bad attachment.
- `suppressed` (400): Every to address is on the suppression list. Suppressed cc/bcc addresses are dropped silently.
- `quota_exceeded` (402): The monthly email quota is used up. It resets on the 1st of the month (UTC).
- `insufficient_scope` (403): Token is missing the send scope.
- `domain_not_linked` (403): The from domain has not been added to the connected account.
- `domain_not_verified` (403): The from domain is added but its DNS records are not verified yet.
- `quarantine_active` (403): Sending is paused for this account. Contact SNDR support.
- `forbidden` (403): A recipient is on a known spam-trap list. Remove it and retry.
- `payload_too_large` (413): The request, or the rendered html + text, is over 16 MiB.
- `send_velocity` (429): Sending too fast for this account. Back off and retry.
- `sends_frozen` (503): Sending is temporarily paused platform-wide. Retry later.
