---
title: "Send an email"
method: POST
path: "/v1/emails"
tags: ["Emails"]
---

# Send an email

`POST /v1/emails`

## Request

- Base URL: `https://smtpfa.st/api`
- URL: `https://smtpfa.st/api/v1/emails`
- Auth: HTTP bearer

Queue a single email for delivery. The email is validated, queued, and sent asynchronously via the configured sending provider. Recipients on domains reserved for documentation and tests (example.com, example.net, example.org, and anything under .test, .example, .invalid or .localhost) are refused with 400; for test sends use an address at simulator.amazonses.com, such as success@simulator.amazonses.com.

## Headers

- `Idempotency-Key` string

## Request body

- SendEmailRequest: from and subject are required, unless a template supplies them. Give a body as html and/or text, or name a published template; not both.
  - `template` object: Send a published template instead of html and text, as in Resend. The published version is rendered with the variables, then every normal send rule applies. from, subject and reply_to in the request win over the template's defaults; a subject in the request is rendered with the same variables.
    - `id` string, required: The template id or alias.
    - `variables` object: Values by variable key. A missing value uses the inline fallback ({{{KEY|default}}}), then the variable's fallback_value. A declared variable with neither must be given a value: otherwise the send answers 422 and lists the keys in missing_variables, before anything is charged or queued. Values are inserted as text: HTML-escaped, and never parsed as Markdown. FIRST_NAME, LAST_NAME and EMAIL fill the contact tags of the same name.
  - `attachments` object[]: Up to 10 files, base64 encoded, 25 MB per message in total. A content_id makes the part inline: reference it from the HTML as cid:<content_id>.
    - `filename` string, required
    - `content` string, required: Base64-encoded file content
    - `content_type` string
    - `content_id` string: Letters, digits, . _ - with an optional @domain; sent inline as multipart/related
    - `inline` boolean: Send inline even without a content_id (a Content-ID is derived from the filename)
  - `from` string, email: Sender email address (must belong to a verified domain)
  - `to` union, required: One recipient address, or a list of them
    - string, email
    - string[]
  - `cc` union: Cc recipients
    - string, email
    - string[]
  - `bcc` union: Bcc recipients
    - string, email
    - string[]
  - `reply_to` union: Reply-To address or addresses
    - string, email
    - string[]
  - `subject` string: Email subject line
  - `tags` object[]: Up to 50 name/value pairs, returned on events and webhooks and filterable in logs
    - `name` string, required
    - `value` string, required
  - `headers` object: Custom headers, e.g. X-Entity-Ref-ID
  - `scheduled_at` string, date-time: Send later, up to 30 days ahead (ISO 8601)
  - `category` 'transactional' | 'marketing': Defaults to marketing. Label receipts, password resets and other transactional mail transactional so a marketing opt-out does not block them; unlabelled mail is treated as marketing. Over SMTP, set the X-SMTPfast-Category header instead; it is removed before sending. An email whose html or text (for a template, the rendered body) contains an unsubscribe placeholder is always marketing. A marketing email carries the one-click List-Unsubscribe header and is not sent to anyone who unsubscribed. A transactional email has no List-Unsubscribe header and still goes to addresses whose suppression has scope marketing (a recipient's own unsubscribe, or a manual suppression created with scope marketing), but not to addresses with scope all: hard bounces, complaints, and manual suppressions, which block everything by default. Transactional means mail the recipient needs because of an action or account; labelling promotions transactional is not allowed. Any other value is refused with 400.
  - `track_clicks` boolean: Not in Resend, which sets tracking per domain. Rewrite this email's links through the click tracker (true) or send them exactly as written (false). Left out, the account's tracking settings apply, including the setting for transactional email, except that a contact-form relay (every recipient on the From domain or a parent or subdomain of it, Reply-To on an unrelated domain, not sent from an inbox) gets no click tracking. Over SMTP, set the X-SMTPfast-Track-Clicks header to true or false instead; it is removed before sending. To keep one link out of tracking, such as a password reset link with a token, add data-smtpfast-track="off" to its <a> tag; the attribute is removed before sending. null is the same as leaving it out; any other value that is not true or false is refused with 400.
  - `track_opens` boolean: Not in Resend. Add the open-tracking pixel to this email (true) or not (false). Left out, the account's tracking settings apply. Over SMTP, set the X-SMTPfast-Track-Opens header to true or false instead; it is removed before sending. null is the same as leaving it out; any other value that is not true or false is refused with 400.
  - `html` string: HTML body of the email. Include `{{unsubscribe_url}}` (or the Resend-compatible `{{{RESEND_UNSUBSCRIBE_URL}}}`) anywhere in the body and SMTPfast substitutes the per-recipient unsubscribe link at send time. An email with the placeholder is always marketing, and a marketing email gets the same URL on the RFC 8058 `List-Unsubscribe` header.
  - `text` string: Plain text fallback body. Supports the same `{{unsubscribe_url}}` placeholder as the HTML body.

## Response `200`

Email accepted. A repeat with the same Idempotency-Key and body returns the same id.

- SendEmailResponse: The id of the accepted email, as Resend returns it. Fetch the email with GET /v1/emails/{id}.
  - `id` string, required

## Other responses

- `400`: Bad request - check your payload
- `401`: Invalid or missing API key
- `403`: Forbidden - domain not verified or insufficient permissions
- `429`: Rate limit exceeded - retry after the period indicated in the Retry-After header
- `500`: Internal server error

---

[API](https://skmtc.dev/smtpfa/apis/smtpfast-api.md) · [All operations](https://skmtc.dev/smtpfa/apis/smtpfast-api/llms.txt) · [OpenAPI document](https://skmtc.dev/smtpfa/apis/smtpfast-api/revisions/79dda742f53e?raw)
