API Overview
Everything the Delivr web client does goes through a public, documented REST API. You can use the same API to script your mail, build integrations, or write your own client.
Base URL and versioning
All routes are versioned and live under /v1 on your API domain:
https://api.mail.example.com/v1
Locally, that's http://localhost:14123/v1. Breaking changes will only ever ship under a new version prefix.
The health check lives outside the versioned API:
GET /health
Interactive reference
Every route is described with OpenAPI and browsable in an interactive Scalar reference on your own instance — unless the operator turned it off with DLA_DISABLE_DOCS=true:
| URL | What it is |
|---|---|
https://api.mail.example.com/docs/v1 | Interactive reference — try requests in the browser |
https://api.mail.example.com/docs/v1/openapi | Raw OpenAPI document (JSON) |
The reference is the source of truth for request and response schemas. This page gives you the big picture.
Authentication
Send a token in the Authorization header with every request:
Authorization: Bearer <token>
There are two kinds of tokens:
| Token | Prefix | How you get it | Lifetime |
|---|---|---|---|
| Session token | dla_sess_ | POST /v1/auth/login with username and password | 7 days, or until logout or a password change |
| API key | dla_apikey_ | Created in Settings → API Keys or via /v1/account/apikeys | Until it expires (optional) or is deleted |
Both are 256-bit random secrets. The server stores only Argon2id hashes, so a token is shown exactly once — when it's created.
Signing in with a session
curl -X POST https://api.mail.example.com/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "anna", "password": "…"}'
The response contains the session token. Check it with GET /v1/auth/session and end it with POST /v1/auth/logout.
Failed sign-ins are rate-limited; too many attempts return 429 Too Many Requests with a Retry-After header.
Using an API key
curl https://api.mail.example.com/v1/mail-accounts \
-H "Authorization: Bearer dla_apikey_…"
API keys act with the same permissions as the user who created them.
Response envelope
Every response uses the same JSON envelope:
{
"success": true,
"code": 200,
"message": "OK",
"data": { }
}
Errors keep the shape, with success: false and a human-readable message. For security, validation errors don't echo details about your input.
| Status | Meaning |
|---|---|
400 | The request is invalid or exceeds a limit (e.g. attachment size). |
401 | Missing, invalid, or expired token. |
403 | Authenticated, but not allowed (e.g. admin routes). |
404 | The resource doesn't exist or isn't yours. |
409 | Conflict, e.g. a username that's taken. |
429 | Rate limit hit. |
Resources
Resources are nested the same way your mail is: accounts contain mailboxes, mailboxes contain mails, mails contain attachments.
/v1
├── /auth login · session · logout · reset-password
├── /account profile · password · apikeys · preferences
├── /mail-accounts
│ └── /{mailAccountID}
│ ├── /credentials update IMAP/SMTP connection data
│ ├── /identities sender addresses and signatures
│ ├── /special-use Sent / Drafts / Trash / Spam / Archive mapping
│ ├── /search cross-folder search
│ └── /mailboxes
│ └── /{mailboxPath}
│ ├── /status
│ ├── /mail-bulk-actions move · copy · delete · flags
│ └── /mails
│ └── /{mailUID}
│ ├── /send · /move · /flags
│ └── /attachments/{attachmentId}
├── /bimi/{domain} sender brand logo lookup
└── /admin/users user management (admins only)
A few conventions:
mailboxPathis the IMAP path of the folder, URL-encoded — e.g.INBOX, orINBOX%2FReceiptsforINBOX/Receipts.mailUIDis the IMAP UID of the message within its mailbox.- Lists are paginated with
limit(1–100, default 10),offset, andorder(newestoroldest). - Attachments are addressed by their index within the message and streamed with
Cache-Control: no-store.
Common tasks
List the latest messages in the Inbox
curl "https://api.mail.example.com/v1/mail-accounts/1/mailboxes/INBOX/mails?limit=20" \
-H "Authorization: Bearer $DELIVR_API_KEY"
Search across all folders
curl "https://api.mail.example.com/v1/mail-accounts/1/search?from=billing@acme.com&hasAttachment=true" \
-H "Authorization: Bearer $DELIVR_API_KEY"
Search supports text, subject, from, to, body, since and before (Unix timestamps in milliseconds), and the filters hasAttachment, seen, flagged, answered, and draft.
Compose and send
Sending is a two-step process, mirroring how mail clients work with IMAP:
- Create the message in a mailbox (usually Drafts) with
POST …/mailboxes/{mailboxPath}/mails. Send JSON, ormultipart/form-datawith the message as a JSONmailfield plus one or moreattachmentsfiles. - Send it with
POST …/mails/{mailUID}/send. By default (moveToSent: true), Delivr then files it into your Sent folder and reports the outcome assavedToSent.
The combined attachment size is limited by the instance's DLA_MAX_ATTACHMENT_SIZE_MB (25 MB by default).
Bulk actions
Move, copy, delete, or flag many messages in one request:
curl -X POST "https://api.mail.example.com/v1/mail-accounts/1/mailboxes/INBOX/mail-bulk-actions/move" \
-H "Authorization: Bearer $DELIVR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"uids": [4211, 4212, 4215], "targetMailbox": "Archive"}'
Check the interactive reference for each action's exact body.
Generated TypeScript client
Delivr Web doesn't hand-write API calls — it uses a type-safe client generated from the OpenAPI document with Hey API. You can generate one for your own TypeScript project the same way:
npx @hey-api/openapi-ts \
-i https://api.mail.example.com/docs/v1/openapi \
-o src/delivr-client
Any other OpenAPI generator works too — for Python, Go, Rust, and more.