Send an email
POST /api/v1/email/send takes a template id, a recipient and the values of its variables.
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. |
{
"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:
{
"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
{ "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 for the full list.
Full example
// 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;
}