CONNECT YOUR APPLICATION
A useful message.
An explicit contract.
The private foundation’s transactional API prepares messages from published templates. A durable identifier lets you follow an intent without mistaking it for delivery.
Explore this guidePrivate project · public opening in preparation
202 means queued
A recorded intent
A durable message identifier
Delivery tracked separately
Information updated on 14 October 2026. These guides describe product choices and private integration work. They are not a certification, reviewed legal terms or a qualification of live providers.
KEYS AND SCOPE
A key for a purpose, scoped to a workspace.
The contract uses Authorization: Bearer with a service key created in the application. Its scopes, workspace and expiry limit operations. None of the examples contains a usable key.
messages.send authorises a transactional intent; messages.read reads a message and its events. statistics.read and events.write cover their respective endpoints. A key does not replace human approval of a marketing campaign.
Intended canonical base: https://mailie.app/api/v1. The routes exist in the private code; this documentation does not announce commercial availability or qualification of live transport.
A DURABLE INTENT
Published template, verified sender, idempotency key.
The POST requires a recipient, an authorised sender, an already published transactional template version and a compatible category. Labelling a promotional email as a service message does not make it one.
Use the same Idempotency-Key for the same logical operation and body. A new key is a new intent. Reusing the key with a different body causes a conflict.
POST /api/v1/messages HTTP/1.1
Host: mailie.app
Authorization: Bearer MAILIE_SERVICE_KEY_PLACEHOLDER
Idempotency-Key: demo-order-42
Content-Type: application/json
{
"type": "transactional",
"category": "order_confirmation",
"fromSenderId": "11111111-1111-4111-8111-111111111111",
"to": {
"email": "recipient@example.invalid",
"name": "Recipient example"
},
"template": {
"id": "22222222-2222-4222-8222-222222222222",
"version": 1
},
"variables": {
"reference": "ORDER-EXAMPLE-42"
},
"metadata": {
"external_reference": "demo_order_42"
}
}Follow the same intent
GET /api/v1/messages/{messageId}
GET /api/v1/messages/{messageId}/events
Authorization: Bearer MAILIE_READ_KEY_PLACEHOLDERUse the message_id returned by the actual response. Reading these endpoints requires messages.read.
The response includes status, mode and request_id. simulation and test_sink are distinct from live.
The UUIDs and example.invalid addresses are fictional. Prepare an isolated workspace, a template and an authorised test transport before testing; transport mode is decided on the server, not through an arbitrary client field.
READING THE RESPONSE
Accepting a command is not delivering an email.
202 indicates that the message and durable queue entry were created. Read the status and events using the returned identifier. Transport acceptance remains distinct from delivery to the recipient.
A lost response around submission may leave an uncertain state. Check the existing intent and its events before deciding to resend. Individual opens and clicks are not available success criteria.
EVENTS AND LIMITS
Plan for errors and revocation.
Errors use a Problem Details format with a code and request reference. Limits and quotas come from the workspace policy; this page does not promise unlimited capacity.
Client webhooks in the current work use a signature and event identifier. A duplicated callback must remain idempotent. Validate the signature and retain the logical identity before producing an effect in your system.
| Method | Route | Scope | Purpose |
|---|---|---|---|
| POST | /messages | messages.send | One transactional intent |
| POST | /messages/batch | messages.send | A bounded batch; inspect each result |
| GET | /messages/{messageId} | messages.read | Read a visible message |
| GET | /messages/{messageId}/events | messages.read | Read operational events |
| GET | /statistics | statistics.read | Read operational aggregates |
| POST | /business-events | events.write | Record an authorised business event |
A retryable HTTP error does not prove that a previous submission was never accepted. Retain the logical identifier and reconcile any uncertain outcome.
No downloadable OpenAPI link is shown until the project’s official artifact is delivered. These examples follow the code’s routes and schemas; contracts may still change before public opening.
FOR AUTHORISED PRIVATE ACCOUNTS
