--- type: API title: Transactional email API description: Send one-off transactional mail (password resets, receipts, alerts) via POST /api/v1/transactional_emails. Domain-bound API keys, no unsubscribe headers, GDPR suppression honored. resource: https://euromailing.com/api/v1/transactional_emails tags: [transactional, email, api] timestamp: 2026-07-06T00:00:00Z --- # Transactional email API Send a single transactional message (password reset, receipt, alert) through Euromailing's delivery pipeline. Mail is DKIM-signed by the customer's verified domain and carries a proper Message-ID. Transactional mail deliberately differs from campaign mail: - **No unsubscribe headers** are added, and any `List-Unsubscribe*` headers supplied by the caller are stripped. - **Marketing unsubscribes are ignored** — a recipient who opted out of newsletters still receives their receipt. - **The GDPR suppression list IS honored** — erased addresses are refused with `recipient_suppressed`. ## Prerequisites (one-time, in the Euromailing dashboard) 1. Verify a sending domain (DKIM/SPF/DMARC DNS records) under *Sending domains*. 2. Create an API key under *Account → API keys* with the `transactional:send` scope, **bound to that sending domain**. A key not bound to a domain cannot send transactional mail. 3. The account needs an active subscription (or complimentary status); otherwise every send returns HTTP 402. ## Request ``` POST https://euromailing.com/api/v1/transactional_emails Authorization: Bearer eml_live_… Content-Type: application/json ``` | Field | Type | Required | Notes | |---------------|--------|----------|-------| | `from` | string | yes | `"Name "` or bare address. The domain MUST equal the API key's bound sending domain. | | `to` | string | yes | Exactly one recipient address. Loop for multiple recipients. | | `subject` | string | yes | | | `html_body` | string | one of these two | A plain-text alternative is auto-derived from HTML when `text_body` is absent. | | `text_body` | string | one of these two | Text-only mail is fully supported. | | `reply_to` | string | no | | | `headers` | object | no | Custom headers (e.g. `In-Reply-To` for threading). `From/To/Cc/Bcc/Subject`, `List-Unsubscribe*` and `X-EML-Meta-*` are silently stripped. A caller-supplied `Message-ID` is preserved. | | `attachments` | array | no | `[{ "filename": "invoice.pdf", "content_type": "application/pdf", "content": "", "content_id": "optional-cid" }]` — 10 MB total decoded size. Set `content_id` to make the attachment **inline (CID)**: reference it in `html_body` as `` and the message is assembled as multipart/related. | ### Example ```bash curl -X POST https://euromailing.com/api/v1/transactional_emails \ -H "Authorization: Bearer $EUROMAILING_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "from": "MyApp ", "to": "user@example.com", "subject": "Reset your password", "html_body": "

Reset

" }' ``` ### Inline (CID) image example ```json { "from": "MyApp ", "to": "user@example.com", "subject": "Welcome", "html_body": "

\"Logo\" Welcome!

", "attachments": [ { "filename": "logo.png", "content_type": "image/png", "content": "", "content_id": "logo" } ] } ``` ## Response Success is `202 Accepted`: ```json { "id": "", "status": "queued", "from": "MyApp ", "to": "user@example.com" } ``` ## Errors All errors use the envelope `{ "error": { "code": "...", "message": "...", "details": {} } }`. | HTTP | `error.code` | Meaning / fix | |------|--------------|---------------| | 401 | `missing_credentials`, `invalid_credentials` | Send `Authorization: Bearer `. | | 402 | `payment_required` | Account has no active subscription — choose a plan in the dashboard. | | 402 | `email_limit_reached` | Monthly plan volume used up — upgrade the plan or wait for the next month. | | 403 | `insufficient_scope` | Key lacks `transactional:send`. | | 422 | `key_not_domain_bound` | Bind the API key to a sending domain in the dashboard. | | 422 | `sending_domain_unverified` | Verify the domain's DNS records first. | | 422 | `from_domain_mismatch` | The From address must be on the key's bound domain (`details.allowed_domain`). | | 422 | `invalid_recipient` | `to` must be a single valid email address. | | 422 | `recipient_suppressed` | Recipient requested GDPR erasure — do not retry. | | 422 | `subject_required`, `body_required`, `from_required` | Missing field. | | 422 | `invalid_attachment`, `attachments_too_large` | Base64 must be strict; 10 MB total limit. | | 429 | `rate_limited` | 60 requests/minute per key. Honor `Retry-After`; queue sends through a background job. | | 502 | `injection_failed` | The mail server refused the message — safe to retry with backoff. | ## Recommended integration patterns - **Rails**: use the `euromailing-rails` gem — existing ActionMailer mailers work unchanged. See [the Rails guide](../guides/rails-gem.md). - **Anything else**: one HTTPS POST per message, retry on 429/5xx with exponential backoff, never retry `recipient_suppressed` or `from_domain_mismatch`. - Send from a background job/queue so the per-key rate limit (60/min) degrades gracefully under burst.