---
title: "Send or schedule an email"
method: POST
path: "/email"
tags: ["Emails"]
---

# Send or schedule an email

`POST /email`

## Request

- Base URL: `https://www.unitpost.com/api/v1`
- URL: `https://www.unitpost.com/api/v1/email`
- Auth: HTTP bearer

Send a single transactional email — now, or at a future time by passing `scheduled_at`. Provide the content inline as `html`/`text`, or reference a saved template with `template` (create one with [Create a template](#post-email-templates)). Pass an `Idempotency-Key` header to make retries safe: the same key replays the original result instead of sending twice. The response returns the new email's `id` — read its delivery status back with [Retrieve an email](#get-email-id), change a scheduled send with [Cancel or reschedule an email](#patch-email-id), or send many at once with [Send a batch of emails](#post-email-batch). A template in the marketing category sends to one recipient (no cc/bcc) and adds them to your contacts if they aren't one yet, because the unsubscribe link belongs to a contact: the new contact counts toward a Free plan's contact limit (at the limit the send is refused) and fires `contact.created` with `source: "send"`. Requires the `emails:send` capability.

## Request body

- SendEmail
  - `from` string, required: Sender address on one of your verified domains. Accepts a bare address ("hi@acme.com") or a display name ("Acme <hi@acme.com>") — the name is what inbox clients show next to the message.
  - `to` union, required
    - string
    - string[]
  - `subject` string
  - `cc` union
    - string
    - string[]
  - `bcc` union
    - string
    - string[]
  - `reply_to` union
    - string
    - string[]
  - `in_reply_to` string
  - `headers` object: Custom headers (your own `X-*` and threading headers). Envelope (`From`/`To`/`Cc`/`Bcc`/`Reply-To`/`Subject`/`Date`/`Message-ID`) and signing/transport headers are managed for you and rejected with 422. On a non-marketing send you may set your own `List-Unsubscribe` (one or more `<…>` entries, each `https://` or `mailto:`) and `List-Unsubscribe-Post` (exactly `List-Unsubscribe=One-Click`, which needs at least one `https://` entry). A marketing-template send refuses both: Unitpost adds its own per recipient.
  - `tags` object[]
    - `name` string, required
    - `value` string, required
  - `template` object: Published template plus variable values. Mutually exclusive with `html` and `text`.
    - `id` string, required
    - `variables` object
  - `html` string: HTML MIME body. It is sanitized for email safety and may be supplied with `text` to create a multipart alternative. This field does not parse Markdown.
  - `text` string: Plain-text MIME body. Newline characters are preserved; Markdown and rich-text styling are not interpreted. It may be supplied with `html` as the fallback alternative. When `html` is sent without `text`, a plain-text alternative is derived from the visible HTML content; an explicit `text` value is always sent unchanged.
  - `attachments` object[]
    - `filename` string, required
    - `content` string
    - `path` string, uri
    - `content_type` string
    - `content_id` string
  - `open_tracking` boolean
  - `click_tracking` boolean
  - `scheduled_at` string, date-time

## Response `200`

Accepted for delivery or scheduled.

- SendResult
  - `id` string, required
  - `status` 'queued' | 'scheduled', required
  - `scheduled_at` string, nullable, required

## Other responses

- `401`: Missing or invalid API key.
- `403`: Insufficient scope, or sending domain not found / not verified, or (marketing template) adding the recipient as a contact would pass the Free plan's contact limit (`limit_reached`).
- `404`: Template not found.
- `409`: Idempotency conflict (same key, different body / in-flight).
- `422`: Validation error, or (marketing template) the recipient is an older or secondary address of another contact, so a marketing email can't be sent to it.
- `429`: Too many requests, or sending is paused: `rate_limit_exceeded` (request rate), `quota_exceeded` (free-tier daily/monthly cap, or the plan's monthly sending allowance and any purchased credit are spent), or `overage_cap_exceeded` (a paid plan reached its overage cap — sending resumes when the allowance refreshes, or buy a credit top-up). Includes a `Retry-After` header with the seconds until the window resets (the billing period end for quota/overage blocks).

---

[API](https://skmtc.dev/unitpost/apis/unitpost-api.md) · [All operations](https://skmtc.dev/unitpost/apis/unitpost-api/llms.txt) · [OpenAPI document](https://skmtc.dev/unitpost/apis/unitpost-api/revisions/86ff7c890967?raw)
