---
title: Send an email
slug: send-email
description: POST /api/v1/email/send takes a template id, a recipient and the values of its variables.
order: 2
updatedAt: '2026-08-14'
---

```
POST https://sentfa.st/api/v1/email/send
Content-Type: application/json
x-api-key: YOUR_API_KEY
```

## Request body

| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string | Yes | Id of the email template to send, for example `em_a1b2c3d4`. It must belong to the project that owns the key. |
| `to` | string | Yes | Recipient address. |
| `cc` | string[] | No | Addresses in copy. |
| `params` | object | No | Values for the template variables. Keys are the variable names, values are strings. |
| `attachments` | object[] | No | Files to attach. See below. |

```json
{
  "id": "em_a1b2c3d4",
  "to": "customer@example.com",
  "cc": ["billing@example.com"],
  "params": {
    "user_name": "Alex",
    "invoice_number": "2026-0184"
  }
}
```

## Variables

The subject and the body of a template can contain placeholders written as
`{{variable_name}}`. At send time each one is replaced with the matching key
from `params`.

Keys you do not send are left as they are in the template, so an invoice email
that expects `{{invoice_number}}` and does not receive it will go out with the
placeholder visible. Send every variable the template declares.

## Attachments

Each attachment needs a `filename` and the file itself, given either as a public
URL or as base64:

```json
{
  "id": "em_a1b2c3d4",
  "to": "customer@example.com",
  "attachments": [
    { "filename": "invoice.pdf", "url": "https://example.com/invoices/2026-0184.pdf" },
    { "filename": "terms.txt", "base64": "VGVybXMgYW5kIGNvbmRpdGlvbnM=", "contentType": "text/plain" }
  ]
}
```

A URL must be absolute and use `http` or `https`; anything else is rejected with
`400`. The file also has to be reachable from the internet without
authentication — a URL that cannot be fetched makes the send fail.

## Response

```json
{ "ok": true, "message": "Email \"em_a1b2c3d4\" queued/sent to customer@example.com" }
```

`ok: true` means SentFast handed the message to the delivery provider and wrote
the send to your logs. It is not a guarantee that the recipient's mail server
accepted it — that is why the log exists.

Every failure uses the same shape with `ok: false` and an `error` string. See
[Errors](/docs/errors) for the full list.

## Full example

```js
// Node.js — no dependencies
async function sendWelcomeEmail(email, name) {
  const res = await fetch('https://sentfa.st/api/v1/email/send', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': process.env.SENTFAST_API_KEY,
    },
    body: JSON.stringify({
      id: 'em_a1b2c3d4',
      to: email,
      params: { user_name: name },
    }),
  });

  const data = await res.json();
  if (!data.ok) throw new Error(`SentFast: ${data.error}`);
  return data;
}
```
