Documentation menu

REST API

The JSON API under /api for listing, reading, searching, downloading, relaying, and deleting mail — plus the Socket.IO events the web inbox uses for live updates.

Reference documentation, maintained in the maildev repository.

MailDev provides an easily consumable REST API. All routes are served under the /api prefix at http://localhost:1080 unless configured otherwise. If a --base-pathname (or basePathname) is set, it is prepended to the prefix (e.g. /maildev/api). All data is returned as JSON unless specified.

Example email response

Get all emails:

$ curl 'http://localhost:1080/api/email'

Returns:

[
    {
        "id": "XwgKAxto",
        "time": "2026-01-05T19:02:09.156Z",
        "read": false,
        "subject": "The ex-presidents are surfers",
        "from": [
            {
                "address": "angelo.pappas@fbi.gov",
                "name": "Angelo Pappas"
            }
        ],
        "to": [
            {
                "address": "johnny.utah@fbi.gov",
                "name": "Johnny Utah"
            }
        ],
        "cc": [],
        "date": "2026-01-05T19:02:09.000Z",
        "text": "The wax at the bank was surfer wax!!!",
        "html": "<!DOCTYPE html><html><head></head><body><p>The wax at the bank was surfer wax!!!</p></body></html>",
        "headers": {
            "content-type": "multipart/mixed; boundary=\"--_boundary\"",
            "from": "Angelo Pappas <angelo.pappas@fbi.gov>",
            "to": "Johnny Utah <johnny.utah@fbi.gov>",
            "subject": "The ex-presidents are surfers",
            "message-id": "<1412535729142-cc4cb0f1@fbi.gov>",
            "date": "Sun, 05 Jan 2026 19:02:09 +0000",
            "mime-version": "1.0"
        },
        "priority": "normal",
        "attachments": [
            {
                "filename": "attachment-1.txt",
                "generatedFileName": "attachment-1.txt",
                "contentType": "text/plain",
                "contentDisposition": "attachment",
                "contentId": "0958713110a99ea2afc3b117c9d5feb3@maildev",
                "size": 24
            }
        ],
        "envelope": {
            "from": { "address": "angelo.pappas@fbi.gov" },
            "to": [{ "address": "johnny.utah@fbi.gov" }],
            "host": "djf-3.local",
            "remoteAddress": "127.0.0.1"
        },
        "size": 1024,
        "sizeHuman": "1 KB"
    }
]

Endpoints

All paths are relative to the /api prefix.

GET /api/email/summary - Get a page of email summaries, newest first (see Listing a large inbox)

GET /api/email - Get all emails (supports filtering and pagination)

GET /api/email/:id - Get a given email by id (marks it as read)

DELETE /api/email/:id - Delete a given email by id

POST /api/email/delete - Delete multiple emails by id

DELETE /api/email/all - Delete all emails

PATCH /api/email/read-all - Mark all emails as read (returns the count)

GET /api/email/:id/html - Get a given email's HTML body with embedded attachments

GET /api/email/:id/source - Get the raw email source (RFC 822)

GET /api/email/:id/download - Download a given email as an .eml file

GET /api/email/:id/attachment/:filename - Get a given email's file attachment

POST /api/email/:id/relay/:relayTo? - If configured, relay a given email to its real "to" address, or to the optional relayTo recipient override

GET /api/reloadMailsFromDirectory - Reload emails from the configured mail directory

GET /api/config - Get the application configuration

GET /api/healthz - Health check

Bulk Delete

The POST /api/email/delete endpoint deletes a specific set of emails in one request. The request body must include an ids array.

{
    "ids": ["XwgKAxto", "29wQJq2q"]
}

It returns the IDs that were deleted and any IDs that were not found:

{
    "deleted": ["XwgKAxto"],
    "notFound": ["29wQJq2q"]
}

Real-time updates

In addition to the REST API, the web server exposes a Socket.IO endpoint at /socket.io for real-time notifications. It emits a newMail event when an email arrives and a deleteMail event when one is removed.

The newMail payload is a summary (the same shape as GET /api/email/summary returns), not the full email — fetch GET /api/email/:id if you need the body.

Listing a large inbox

GET /api/email returns every email in full, bodies included. That is convenient for a handful of messages and expensive for thousands: a 10,000 email inbox serializes to well over 100 MB.

GET /api/email/summary returns a bounded page of summaries instead — no html, text or headers — along with the counts needed to paginate. This is what the web interface uses.

GET /api/email/summary?skip=0&limit=50&search=welcome&sort=desc&unread=true
Parameter Default Description
skip 0 Matching emails to skip
limit 50 Page size, clamped to 200
search Case-insensitive match on subject, participants, body
sort desc desc for newest first, asc for oldest first
unread true to return only unread emails
{
    "items": [
        {
            "id": "abc123",
            "time": "2026-07-27T09:12:44.000Z",
            "read": false,
            "subject": "The ex-presidents are surfers",
            "size": 3072,
            "sizeHuman": "3 KB",
            "from": [
                { "address": "angelo.pappas@fbi.gov", "name": "Angelo Pappas" }
            ],
            "to": [{ "address": "johnny.utah@fbi.gov", "name": "Johnny Utah" }],
            "attachmentCount": 1,
            "preview": "The wax at the bank was surfer wax!!!"
        }
    ],
    "total": 1024,
    "storeTotal": 1024,
    "unread": 17,
    "skip": 0,
    "limit": 50
}

total counts the emails matching search; storeTotal and unread describe the whole store and ignore the search.

Pagination

The GET /api/email endpoint allows for simple skip/limit pagination.

GET /api/email?skip=10&limit=25

Without limit it returns every email from skip onwards. Emails come back in arrival order unless sort is given, in which case they are ordered by received time — sort=desc for newest first, sort=asc for oldest first. Combine it with limit to fetch the most recent emails:

GET /api/email?limit=25&sort=desc

Filtering

The GET /api/email endpoint allows simple filtering. Any query parameter that isn't a reserved keyword (skip, limit) is treated as an exact-match filter against a field of the returned email. Nested fields can be addressed with dot syntax (from.address=value).

For example:

GET /api/email?subject=Big wave coming # only emails with the exact subject

GET /api/email?from.address=angelo.pappas@fbi.gov # only emails from this sender

GET /api/email?read=false&subject=test # only unread emails with the exact subject

Edit in the maildev repoView as markdown