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:
htmlContentand/ortextContent, withsubject;- 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 overrideto,cc,bcc,replyTo,params(merged with the request's),subject,htmlContentandtextContent. The response then containsmessageIds;attachment:[{ "name": "invoice.pdf", "content": "<base64>" }]or[{ "url": "https://…/invoice.pdf" }]. The URL must be public (http or https); withoutname, 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).