# Sending email

> Compose transactional email, address recipients, use templates, and retry safely with idempotency keys.

Send email with a single request to [`POST /v1/emails/send`](/docs/api/emails/send). The sender must be on a [verified domain](/docs/learn/domains), and the API key needs the `email.send` permission.

## Compose a message

| Field | Type | Description |
| --- | --- | --- |
| `from` | string · required | A bare address on a verified domain, such as `hello@mail.example.com`. |
| `fromName` | string · optional | The display name recipients see, such as `Acme`. |
| `subject` | string | Required unless you send a template. Up to 500 characters. |
| `html` | string | HTML body. Provide `html`, `text`, or both. |
| `text` | string | Plain-text body. Including it helps deliverability and accessibility. |
| `replyTo` | string · optional | Where replies should go, such as your support inbox. |
| `metadata` | object · optional | Your own values to keep with the message, such as an order ID. |

## Recipients

`to` accepts one address or an array of up to 100. Each address gets its own delivery, with its own status, events, and delivery ID — recipients never see each other.

`cc` and `bcc` are supported when there is a single `to` address. To send the same message to many people, put each one in `to` instead.

## Templates

Design a template in the dashboard under **Email → Templates**, publish it, then send it with `templateId`. The subject and content come from the published template; `templateVariables` fills its variables with string, number, or boolean values. See [Templates](/docs/learn/templates).

```json
{
  "from": "hello@mail.example.com",
  "to": "reader@example.com",
  "templateId": "YOUR_TEMPLATE_ID",
  "templateVariables": {
    "first_name": "Sam",
    "trial_days": 14
  }
}
```

## Schedule a send

Add `scheduledAt` to send later, up to 30 days ahead. Use an ISO 8601 timestamp with a timezone offset:

```json
{
  "from": "hello@mail.example.com",
  "to": "reader@example.com",
  "subject": "Your trial ends tomorrow",
  "text": "Upgrade any time from your dashboard.",
  "scheduledAt": "2026-10-05T09:00:00+01:00"
}
```

Each delivery has status `scheduled` until its time, then is sent within about a minute. Until then you can:

- [Reschedule it](/docs/api/emails/reschedule) to a new time.
- [Cancel it](/docs/api/emails/cancel), so it is never sent.

Both use the `messageId` from the send response. To change the content, cancel it and send a new email.

A scheduled send uses your sending allowance on the day it is sent, not the day you schedule it. If your allowance is used up at that time, its deliveries become `failed` with the reason `quota_exceeded`, nothing is charged, and [email.failed](/docs/webhooks/emails/failed) fires. Subscribe to [email.scheduled](/docs/webhooks/emails/scheduled) and [email.canceled](/docs/webhooks/emails/canceled) to follow schedule changes.

## Idempotency keys

Every send requires an `Idempotency-Key` header. It guarantees a message is sent at most once, even when your request times out and you retry it.

- Use a key that identifies the operation in your system, such as `welcome-user-123` or `receipt-order-8841`.
- Retrying with the same key and the same request returns the original result without sending again.
- Reusing a key with a different request is rejected with `400`, so a bug cannot silently drop a different email.
- Keys are scoped to your project.

Store the key with the operation that triggers the email, so a retry after a crash or deploy uses the same key. See [Retry safely](/docs/learn/retry-safely).

## The response

A successful send returns `202 Accepted`:

```json
{
  "data": {
    "totalQueued": 1,
    "messageId": "emg_example",
    "deliveryIds": ["delivery_example"],
    "scheduledAt": null
  }
}
```

`messageId` identifies the message; `deliveryIds` contains one ID per `to` recipient. `202` means the email is queued, not yet delivered — follow each delivery in [email activity](/docs/learn/email-activity).

## Suppressed recipients

Nexiom Connect protects your sender reputation by not sending to addresses that hard-bounced, reported spam, unsubscribed, or are invalid. Those deliveries are created with status `suppressed` and a `delivery_reason` explaining why, and they don't count toward your sending allowance. See which addresses are suppressed, and why, in the dashboard under **Email → Suppressions** or with [List suppressions](/docs/api/emails/suppressions).

## Sending limits

Sending allowances depend on your plan. When you reach one, the API returns `429` with a `code` describing the limit; retrying will not help until the allowance changes. See [Usage limits](/docs/api/usage-limits).

---

Source: https://connect.nxiom.com/docs/learn/sending
