API Reference
API Reference
GitHub
Inbound Email

Managing Inbound Email

List, fetch, retry, export, and stream inbound email from your project

Mail that arrived on the MX listener is stored per project and read through these routes. They are ordinary /api/v1 endpoints under the inbound permission — an API key carries its project, a session needs the X-Mailyard-Project-Id header.

Listing

GET /api/v1/inbound-emails?limit=50&status=received
ParamNotes
statusOne of the three below
limitDefault 50, maximum 200
beforeCursor: RFC 3339 received_at of the last row you saw
before_idThe id of that row. Send it with before

There are three statuses, and no others:

StatusMeans
receivedStored and parsed
rejectedRefused at ingest — a suppressed sender, or a DMARC failure on a p=reject domain
failedThe MIME tree could not be parsed. The raw bytes are still there

Paging is a keyset cursor, like the outbound log, and for the same reason: this list grows on its own. Send before_id alongside before or two messages sharing a received_at across a page boundary will appear on neither page. There is no total and no offset.

curl -G http://localhost:3000/api/v1/inbound-emails \
  -H "Authorization: Bearer myk_..." \
  --data-urlencode "limit=50" \
  --data-urlencode "status=received"

Counts

GET /api/v1/inbound-emails/stats
{ "counts": { "received": 812, "rejected": 40, "failed": 3 } }

Get an Inbound Email

GET /api/v1/inbound-emails/{id}

{id} is the inbound email UUID. Returns the full record:

{
    "inbound_email": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "project_id": "81af718e-f0ae-4780-a0d7-9f05b34dabcc",
        "domain_id": "3f2504e0-4f89-11d3-9a0c-0305e82c3301",
        "message_id": "abc123@example.com",
        "sender": "sender@example.com",
        "recipients": [
            "inbox@yourdomain.com"
        ],
        "subject": "Hello",
        "text_body": "Plain text body",
        "html_body": "<p>HTML body</p>",
        "headers": {
            "Received": "from mail.example.com ..."
        },
        "attachments": [
            {
                "filename": "invoice.pdf",
                "content_type": "application/pdf",
                "size": 18244
            }
        ],
        "auth": {
            "spf": "pass",
            "dkim": "pass",
            "dmarc": "pass",
            "aligned": true,
            "client_ip": "203.0.113.10"
        },
        "has_raw": true,
        "size": 2048,
        "status": "received",
        "received_at": "2026-01-01T00:00:00Z",
        "created_at": "2026-01-01T00:00:00Z"
    }
}

auth carries the SPF, DKIM and DMARC verdicts stamped at ingest. aligned is the field worth acting on - a valid signature from some other domain is not authentication. See Receiving .

Attachment entries carry metadata only. Fetch the bytes through the download endpoint below.

Delete an Inbound Email

DELETE /api/v1/inbound-emails/{id}

Removes the record and best-effort deletes its blob-stored raw message and attachments. Returns 204 No Content.

Re-dispatch

POST /api/v1/inbound-emails/{id}/retry
{ "emitted": true }

This re-sends the webhook, it does not re-process the mail

The name says retry, and what it actually does is emit the inbound.received webhook event again for a message already stored. The message is not re-parsed and its status does not change.

It works on any stored message regardless of status, and it is not conditional on anything having failed. So it is the tool for “my receiver was down when that arrived” — replaying the notification — and not for recovering a failed parse, which nothing re-runs.

emitted: true means the event was handed to the dispatcher, not that your endpoint answered. Check delivery tracking for that.

Download the Raw Message

GET /api/v1/inbound-emails/{id}/raw

Streams the raw RFC 5322 message as message/rfc822 with a .eml filename. Returns 404 if the raw bytes were not stored.

Download an Attachment

GET /api/v1/inbound-emails/{uuid}/attachments/{idx}

Streams an attachment by zero-based index for an inbound email you own. The response uses the attachment’s original Content-Type and filename. Where the bytes are actually stored depends on MAILYARD_STORAGE_BACKEND, but this endpoint reads them the same way either way.

Live updates

The console shows inbound arrivals as they land, over its own event feed. To be notified machine-side, subscribe to inbound.received with a webhook . It carries metadata only - no bodies or attachments - so fetch those with the endpoints above. See Event Types for the payload.