Transactional email

POST /smtp/email sends through the site's infrastructure: the sending relay (SMTP or SES) configured in the console, the send log, open and click tracking.

{
  "sender": { "name": "My Shop", "email": "contact@my-shop.com" },
  "to": [{ "email": "customer@example.com", "name": "Camille" }],
  "subject": "Your order {{ params.order.id }}",
  "htmlContent": "<p>Thank you {{ contact.FIRSTNAME | default: \"\" }}!</p>",
  "params": { "order": { "id": "1042" } },
  "tags": ["order"]
}

Response 201: { "messageId": "<…@smtp-relay.mailin.fr>" }.

Content

Provide either:

  • htmlContent and/or textContent, with subject;
  • or templateId, the id of a template (see "Templates" below). It must be active; its sender, subject, reply-to, tag and attachment act as defaults.

Without textContent, a text part is derived from the HTML. With textContent only, the HTML part shows the text in a preformatted block.

Sender

sender accepts { "email", "name" } or { "id" } (a sender created with /senders). Without sender, the template's sender is used, otherwise the site's default sender.

Params and template language

params feeds the variables of the content and subject. The contact's attributes (the first to recipient, when it exists in your contacts) are available as contact.*; contact.EMAIL is always set.

Hello {{ contact.FIRSTNAME | default: "customer" }},
Order #{{ params.order.id }}: total {{ params.order.total | floatformat: 2 }} €
{% if params.vip %}Free shipping!{% elif params.country == "BE" %}Shipping: €6.90{% else %}Shipping: €4.90{% endif %}
{% for item in params.items %}{{ forloop.counter }}. {{ item.name }}
{% empty %}No items.{% endfor %}

Filters: default, upper, lower, capfirst, title, truncatechars, length, floatformat, join, escape, safe. Conditions: == != > < >= <= in and or not. Inside a loop: forloop.counter, forloop.counter0, forloop.first, forloop.last. In htmlContent, values are HTML-escaped, except with | safe; the subject and textContent are not escaped.

Recipients, versions, attachments

  • to (required, 99 recipients max), cc, bcc: arrays of { "email", "name" };
  • replyTo: one { "email", "name" } object;
  • tags: array of strings, used to filter statistics;
  • messageVersions: up to 1,000 variants in a single request. Each version can override to, cc, bcc, replyTo, params (merged with the request's), subject, htmlContent and textContent. The response then contains messageIds;
  • attachment: [{ "name": "invoice.pdf", "content": "<base64>" }] or [{ "url": "https://…/invoice.pdf" }]. The URL must be public (http or https); without name, the file name is taken from the URL;
  • headers: custom headers passed through as is.

Idempotency

Add "headers": { "idempotencyKey": "order-1042" }: for 24 h, a repeated request with the same key returns the same messageId (or the same messageIds) without sending again.

Scheduling

scheduledAt (ISO 8601, max 72 h ahead) queues the send: 202 with messageId (messageIds with versions, plus a batchId when there are several). A date in the past or less than 30 seconds ahead sends right away (201).

Action Request
Status GET /smtp/emailStatus/{messageId or batchId}: queued, inProgress, processed or error
Cancel DELETE /smtp/email/{messageId or batchId}: 204, or 404 when the send is no longer scheduled

The messageId contains <, > and @: URL-encode it.

Blocklist

Before sending, every to recipient is checked against the site's transactional blocklist, blocked domains and the platform's suppressions. A blocked recipient receives nothing and produces a blocked event; the request still returns 201. A hard bounce or a spam complaint adds the address to the blocklist automatically.

Action Request
Blocked contacts GET /smtp/blockedContacts (startDate, endDate, senders, limit ≤ 100)
Unblock a contact DELETE /smtp/blockedContacts/{email}
Blocked domains GET /smtp/blockedDomains, POST /smtp/blockedDomains with { "domain": "example.com" }
Unblock a domain DELETE /smtp/blockedDomains/{domain}
Clear hard bounces POST /smtp/deleteHardbounces (contactEmail, startDate, endDate, all optional)

Send errors

Status code Case
400 missing_parameter no htmlContent, textContent or templateId; to or subject missing; no sender
400 invalid_parameter invalid address, inactive template, unknown sender.id, attachment URL unreachable or not public
400 out_of_range more than 99 to, more than 1,000 versions, scheduledAt beyond 72 h
404 document_not_found unknown templateId

Logs and statistics

Need Endpoint
Sent emails GET /smtp/emails (email, messageId, templateId, startDate, endDate, limit ≤ 1000, offset, sort)
One email (body, events) GET /smtp/emails/{uuid}
Events GET /smtp/statistics/events (event, email, messageId, templateId, tags, dates, limit ≤ 5000)
Totals GET /smtp/statistics/aggregatedReport (tag, dates)
Per day GET /smtp/statistics/reports (tag, dates, limit ≤ 30)
Delete a log DELETE /smtp/log/{email or messageId}

For events and reports, dates are passed as startDate and endDate together, or as days; by default, they cover the last 30 days. event values: requests, delivered, opened, clicks, bounces, hardBounces, softBounces, deferred, spam, blocked, invalid, unsubscribed, error. Scheduled or cancelled sends do not appear in /smtp/emails.

Templates

Action Request
List GET /smtp/templates (templateStatus=true or false, limit, offset, sort)
Create POST /smtp/templates: templateName, subject, sender and htmlContent (or htmlUrl) required; replyTo, tag, toField, attachmentUrl, isActive optional. Response 201 { "id" }
Read, update, delete GET, PUT, DELETE /smtp/templates/{templateId}
Send a test POST /smtp/templates/{templateId}/sendTest with { "emailTo": ["…"] } (subject prefixed with [Test])
Rendered preview POST /smtp/template/preview with templateId, params and email (contact whose attributes feed contact.*)

A new template is active by default; pass "isActive": false to create it inactive (it then cannot be used by POST /smtp/email).