Quickstart
#quickstartGet to a working send in four steps — from a fresh deploy to a delivered email.
POST /v1/send with your key in the Authorization: Bearer header. Start with a test key and eftest_ prefix to skip Cloudflare credentials.Authentication
#authenticationAll /v1/send requests must include a bearer token in the Authorization header.
Key types
Prefix: eftest_. Sends are stored in the admin dashboard — no Cloudflare credentials required. Browse, preview HTML, and manage test emails from the built-in Test Mailbox page.
Prefix: eflive_. Sends go through Cloudflare Email Sending. Requires valid CF_API_TOKEN and verified domain.
403.POST /v1/send
#api-sendThe single endpoint for all transactional sends. Use templateSlug / templateId for stored templates, or pass raw html / text directly.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
from | string | required | Sender email address. Must match a verified domain authorized for your key. |
fromName | string | optional | Display name shown in the From header, e.g. Acme Inc. |
to | string or string[] | required | Recipient address or array of up to 50 addresses. Duplicates are deduplicated. |
subject | string | optional | Overrides the template subject. Required for raw html/text sends. |
templateSlug | string | conditional | Stable slug-based template reference. Preferred for application code. |
templateId | string | conditional | Internal ID-based template reference. Use templateSlug when possible. |
themeId | string | optional | Applies a built-in colour theme to layout-based templates. See theme list below. Defaults to default. |
variables | object | optional | Key/value map for {{variable}} interpolation in subject and body. Supports Handlebars loops and conditionals — see templates. |
html | string | conditional | Raw HTML body. Used when not referencing a template. |
text | string | conditional | Plain-text body. Can be combined with html. |
replyTo | string | optional | Reply-to address for the recipient. |
listId | string | optional | Audience list ID. Attaches a one-click List-Unsubscribe header with a per-recipient token, so the recipient can unsubscribe (globally suppressed for all future sends). |
listUnsubscribe | string | optional | Override the unsubscribe URL (e.g. your own https://…/unsubscribe). Passed through verbatim. |
listUnsubscribePost | boolean | optional | Adds List-Unsubscribe-Post: List-Unsubscribe=One-Click (RFC 8058). Defaults to true when listId is set. |
templateSlug, templateId, html, or text must be provided.cURL
fetch (JavaScript)
Response (200)
Unsubscribe
#api-unsubscribeEmailFlare supports one-click unsubscribe via List-Unsubscribe headers (RFC 8058). When a send includes a listId, a per-recipient, one-time token is embedded in the header. Recipients who click it are suppressed from all future sends.
Public endpoint
The unsubscribe endpoint is public — no API key required:
GET returns a confirmation page (for browser clicks); POST returns JSON (for mail-client one-click). Each token is single-use.
Admin API
Manage lists with the admin API (session-authenticated, same as other /api/* routes):
PUBLIC_URL (or public_url in config.toml) to be set. Without it, listId is ignored — pass your own listUnsubscribe URL instead.Templates & themes
#templatesEmailFlare ships 50 layout-based templates built with React Email. Reference them by their templateSlug. Pass themeId to switch colour palettes per send — no CSS edits needed. For custom templates, see the templates guide →
Available themes
defaultoceanforestvioletslateTemplate reference tips
- Prefer
templateSlugovertemplateId— slugs are stable across restores. - Variable names follow the
{{camelCase}}convention used in the template body and subject. - If a variable is missing from the payload, it renders as an empty string — not as the literal
{{variableName}}placeholder. - Custom templates use Handlebars, so loops (
{{#each}}) and conditionals ({{#if}}) work. See the templates guide → themeIdonly applies to layout-based templates. Custom HTML templates ignore it.
Error responses
#errors| Status | Meaning | Common cause |
|---|---|---|
| 401 | Unauthorized | Missing or invalid Authorization header. |
| 403 | Forbidden | Key scope does not authorize the sender domain. |
| 404 | Not found | templateSlug or templateId does not exist. |
| 422 | Validation error | Missing required fields or invalid email format. |
| 429 | Rate limited | Per-key send limit reached. Check X-RateLimit-Reset header. |
| 502 | All recipients failed | Downstream delivery failed for every address in the request. |
| 500 | Server error | Unexpected backend error — check container logs. |
Rate limiting headers returned on every /v1/send response:
Troubleshooting
#troubleshooting401 on /v1/send
Confirm the header is exactly Authorization: Bearer <key> with no extra spaces. Verify the key is marked active on the Keys page.
403 with a domain-scoped key
The from address domain must be included in the key's allowed domains. Create a global scoped key to bypass domain restrictions while debugging.
Template not found (404)
Copy the slug exactly from the Templates page — it is case-sensitive. System templates use slugs like welcome, magic-link, otp.
Theme not applied to email
themeId only works with layout-based (built-in) templates. For custom HTML templates the field is ignored — you control the styles directly in the HTML body.
Sends succeed in test mode but fail in live mode
Test keys (eftest_) store emails in the dashboard and don't require Cloudflare credentials. Live keys use Cloudflare Email Sending — ensure CF_API_TOKEN has the correct permissions and the sender domain is verified. See the Cloudflare token guide →
Test emails are stored in the built-in Test Mailbox page in the admin UI — no external service needed.
Database errors in logs
If you see "This SQL statement is not allowed on /query endpoint", upgrade to the latest EmailFlare image — this was a bug fixed in the backend send route.
docker compose logs -f emailflare. Most issues are visible in the startup or request output.