← Back to docs

Sending Emails

The complete guide to sending transactional email with EuroMail. Single sends, batches, attachments, scheduling, idempotency, and tracking.

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

FieldNotes
fromMust be an address on a verified domain.
toA single address or an array of addresses.
subjectRequired 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_body with every html_body. It is the single cheapest deliverability win.
  • Use an idempotency_key on 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.

Send your first email in about 90 seconds

The free tier includes 1,000 emails a month. All data stays in Finland | GDPR compliance without the paperwork.

Create free account See live delivery data