Overview
Every email you send goes through one endpoint family under /v1/emails. This
guide covers the full surface: a single send, batches, broadcasts, attachments,
scheduling, idempotency, tagging, and how to look up what happened afterwards.
For the exhaustive field-by-field reference, see the Emails API reference.
All requests authenticate with your API key in the X-EuroMail-Api-Key header
and target https://api.euromail.dev.
Prefer SMTP, or integrating WordPress or a legacy app that only speaks SMTP? See Send Email via SMTP. Same pipeline, no code.
Send a single email
curl -X POST https://api.euromail.dev/v1/emails \
-H "X-EuroMail-Api-Key: em_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"from": "[email protected]",
"to": "[email protected]",
"subject": "Welcome aboard",
"html_body": "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
"text_body": "Welcome! Thanks for signing up."
}'
A successful call returns 202 Accepted. The email is queued for delivery, not
yet delivered. The response carries the identifiers you need to track it:
{
"id": "a1b2c3d4-...",
"message_id": "<[email protected]>",
"status": "queued",
"to": "[email protected]",
"sandbox": false,
"scheduled_at": null,
"created_at": "2026-06-13T10:00:00Z"
}
Keep the id. It is the handle for status look-ups,
webhooks, and cancellation.
Required fields
| Field | Notes |
|---|---|
from | Must be an address on a verified domain. |
to | A single address or an array of addresses. |
subject | Required unless you send via a template that sets it. |
You must supply at least one of html_body, text_body, or template_alias.
Always include a text_body alongside html_body. A plain-text part improves
deliverability and serves clients that cannot render HTML.
Recipients, CC, BCC and reply-to
to accepts either a string or an array. cc and bcc are arrays. reply_to
sets the address replies go to when it differs from from:
{
"from": "[email protected]",
"to": ["[email protected]", "[email protected]"],
"cc": ["[email protected]"],
"bcc": ["[email protected]"],
"reply_to": "[email protected]",
"subject": "Your June invoice",
"html_body": "<p>Invoice attached.</p>"
}Attachments
Attachments are base64-encoded inline in the request. Each needs a filename,
the base64 content, and a content_type:
{
"from": "[email protected]",
"to": "[email protected]",
"subject": "Your invoice",
"html_body": "<p>Invoice attached.</p>",
"attachments": [
{
"filename": "invoice-2026-06.pdf",
"content": "JVBERi0xLjQKJ...base64...",
"content_type": "application/pdf"
}
]
}
Keep the total request under the 2 MB body limit. For large files, link to a download instead of attaching.
Idempotency
Network retries can cause duplicate sends. Pass an idempotency_key. A unique
string you generate per logical email, and EuroMail guarantees the email is
accepted at most once. A retry with the same key returns the original result
instead of sending again:
{
"from": "[email protected]",
"to": "[email protected]",
"subject": "Order #4815 confirmed",
"html_body": "<p>Thanks for your order.</p>",
"idempotency_key": "order-4815-confirmation"
}
Use a key derived from your own domain object (order id, invoice id) so the same real-world event always maps to the same key.
Scheduling and cancelling
Set send_at to an RFC 3339 timestamp to schedule delivery for the future. The
response status is scheduled and scheduled_at is populated. See the
Scheduled Sending guide for time-zone handling.
{
"from": "[email protected]",
"to": "[email protected]",
"subject": "Your trial ends tomorrow",
"html_body": "<p>Renew to keep your data.</p>",
"send_at": "2026-06-20T09:00:00Z"
}
Cancel a scheduled email any time before it is sent:
curl -X POST https://api.euromail.dev/v1/emails/{id}/cancel \
-H "X-EuroMail-Api-Key: em_live_your_key_here"Tags and metadata
tags are short labels for grouping and reporting. They power the per-tag
breakdown in Analytics. metadata is an arbitrary JSON
object echoed back on the email and on webhook events, useful for correlating an
email with your own records:
{
"from": "[email protected]",
"to": "[email protected]",
"subject": "Password reset",
"html_body": "<p>Reset link inside.</p>",
"tags": ["auth", "password-reset"],
"metadata": { "user_id": "u_8814", "ip": "203.0.113.7" }
}Open and click tracking
Tracking is controlled per send with the tracking flag (defaults to your
account setting). When enabled, EuroMail rewrites links and inserts an open
pixel, then reports opens and clicks via webhooks and analytics. After a send,
inspect the rewritten links:
curl https://api.euromail.dev/v1/emails/{id}/links \
-H "X-EuroMail-Api-Key: em_live_your_key_here"
For one-to-one transactional mail where tracking is undesirable, set
"tracking": false.
Batch sends
/v1/emails/batch accepts an array of independent messages in one request. Each
is processed on its own; the response reports per-message success and the index
of any failures, so one bad recipient never fails the whole batch:
curl -X POST https://api.euromail.dev/v1/emails/batch \
-H "X-EuroMail-Api-Key: em_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"emails": [
{ "from": "[email protected]", "to": "[email protected]", "subject": "Hi A", "html_body": "<p>A</p>" },
{ "from": "[email protected]", "to": "[email protected]", "subject": "Hi B", "html_body": "<p>B</p>" }
]
}'
The response includes an operation_id you can poll, the successful data
entries, and an errors array of { index, error } for any that were rejected.
Broadcasts and newsletters
To send one message to a whole contact list, with unsubscribe handling and suppression checks applied automatically. Use a broadcast rather than batching addresses yourself. See the Newsletters guide for the end-to-end flow.
Looking up a sent email
Fetch the current status and delivery details of any email by id:
curl https://api.euromail.dev/v1/emails/{id} \
-H "X-EuroMail-Api-Key: em_live_your_key_here"
List recent emails, optionally filtered by status and paged:
curl "https://api.euromail.dev/v1/emails?status=delivered&page=1&per_page=50" \
-H "X-EuroMail-Api-Key: em_live_your_key_here"
Statuses progress queued → sending → delivered, or to bounced / failed.
For the meaning of each state and how retries work, see the
Bounce & Feedback Loops guide. Emails that
exhaust retries land in the Dead Letter Queue.
Sandbox mode
When a send is processed in sandbox mode the response carries "sandbox": true
and no mail leaves the system. Use it to exercise your integration without
touching real inboxes or your reputation.
Best practices
- Send a
text_bodywith everyhtml_body. It is the single cheapest deliverability win. - Use an
idempotency_keyon anything triggered by user actions so retries never double-send. - Tag by purpose (
auth,billing,marketing) to keep analytics legible. - Verify your domain and warm your volume before high-rate sending. See Domain Verification and IP Warm-up.
- Subscribe to webhooks instead of polling for status at scale.