Skip to content

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 guide

Private project · public opening in preparation

THE MAILIE APPROACH

202 means queued

01

A recorded intent

02

A durable message identifier

03

Delivery tracked separately

Useful messages. Clear choices.

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.

HTTPIllustrative request
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_PLACEHOLDER

Use the message_id returned by the actual response. Reading these endpoints requires messages.read.

202Queued, not delivered

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.

Routes under https://mailie.app/api/v1
MethodRouteScopePurpose
POST/messagesmessages.sendOne transactional intent
POST/messages/batchmessages.sendA bounded batch; inspect each result
GET/messages/{messageId}messages.readRead a visible message
GET/messages/{messageId}/eventsmessages.readRead operational events
GET/statisticsstatistics.readRead operational aggregates
POST/business-eventsevents.writeRecord an authorised business event
401Missing, expired or revoked key
403Insufficient permission
404Resource unavailable in this scope
409Idempotency or version conflict
422Invalid message or failed business check
429Quota or rate limit reached
503A required service or capacity is unavailable

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

Return to your workspace.

Your account and permissions determine the available paths.
Open the application