Skip to content

API Reference

Base path: /api/v1. All responses are JSON. All IDs are ULIDs (time-sortable, globally unique).

The full OpenAPI 3.1 spec is also served at runtime at GET /api/v1/openapi.yaml.

REST and WebSocket API for RustMail — a self-hosted SMTP mail catcher. All IDs are ULIDs (time-sortable). All timestamps are ISO 8601 UTC. Base path: /api/v1

Servers

http://localhost:8025Default local server

List messages

GET
/api/v1/messages

Returns messages ordered by created_at descending (newest first). Use limit and offset for pagination. total reflects the count before pagination is applied.

Parameters

Query Parameters

q

Full-text search query (matches subject, body, sender, recipients)

Type
string
limit

Maximum number of results to return

Type
integer
Minimum
1
Maximum
1000
Default
50
offset

Number of results to skip (for pagination)

Type
integer
Minimum
0
Default
0

Responses

Paginated list of message summaries

application/json
JSON
{
  
"messages": [
  
  
{
  
  
  
"id": "01HZ9RQABCDEFGHJKMNPQRSTUV",
  
  
  
"sender": "alice@example.com",
  
  
  
"recipients": "["bob@example.com","carol@example.com"]",
  
  
  
"subject": "Hello world",
  
  
  
"size": 1024,
  
  
  
"has_attachments": false,
  
  
  
"is_read": false,
  
  
  
"is_starred": false,
  
  
  
"tags": [
  
  
  
  
[
  
  
  
  
]
  
  
  
],
  
  
  
"created_at": "2026-03-21T10:00:00Z"
  
  
}
  
],
  
"total": 120
}

Playground

Variables
Key
Value

Samples


Delete all messages

DELETE
/api/v1/messages

Responses

Number of messages deleted

application/json
JSON
{
  
"deleted": 42
}

Playground

Samples


Get a single message with full body

GET
/api/v1/messages/{id}

Responses

Full message including parsed body fields

application/json
JSON
{
  
"id": "01HZ9RQABCDEFGHJKMNPQRSTUV",
  
"sender": "alice@example.com",
  
"recipients": "["bob@example.com","carol@example.com"]",
  
"subject": "Hello world",
  
"size": 1024,
  
"has_attachments": false,
  
"is_read": false,
  
"is_starred": false,
  
"tags": [
  
  
[
  
  
]
  
],
  
"created_at": "2026-03-21T10:00:00Z",
  
"text_body": "string",
  
"html_body": "string"
}

Playground

Samples


Delete a single message

DELETE
/api/v1/messages/{id}

Responses

Message deleted

Playground

Samples


Update message fields

PATCH
/api/v1/messages/{id}

Request Body

application/json
JSON
{
  
"is_read": true,
  
"is_starred": true,
  
"tags": [
  
  
"string"
  
]
}

Responses

Updated message summary

application/json
JSON
{
  
"id": "01HZ9RQABCDEFGHJKMNPQRSTUV",
  
"sender": "alice@example.com",
  
"recipients": "["bob@example.com","carol@example.com"]",
  
"subject": "Hello world",
  
"size": 1024,
  
"has_attachments": false,
  
"is_read": false,
  
"is_starred": false,
  
"tags": [
  
  
[
  
  
]
  
],
  
"created_at": "2026-03-21T10:00:00Z"
}

Playground

Body

Samples


Download raw RFC 822 message source

GET
/api/v1/messages/{id}/raw

Responses

Raw RFC 822 message bytes. Content-Disposition is attachment; filename="<id>.eml".

message/rfc822

Playground

Samples


List attachments for a message (metadata only)

GET
/api/v1/messages/{id}/attachments

Responses

List of attachment metadata

application/json
JSON
{
  
"attachments": [
  
  
{
  
  
  
"id": "01HZ9RQABCDEFGHJKMNPQRFILE",
  
  
  
"message_id": "01HZ9RQABCDEFGHJKMNPQRSTUV",
  
  
  
"filename": "invoice.pdf",
  
  
  
"content_type": "application/pdf",
  
  
  
"size": 48321
  
  
}
  
]
}

Playground

Samples


Download an attachment

GET
/api/v1/messages/{id}/attachments/{aid}

Responses

Attachment file. Content-Type matches the attachment's stored content_type. Content-Disposition is attachment; filename="".

*/*

Playground

Samples


Get an inline image by Content-ID

GET
/api/v1/messages/{id}/inline/{cid}

Returns the inline image matching the given Content-ID. Used by the HTML preview to resolve cid: references. Only serves image/* types; falls back to application/octet-stream otherwise.

Responses

Inline image content with appropriate Content-Type and immutable cache headers.

image/*

Playground

Samples


Export

Download messages in alternative formats


Export a message as EML or JSON

GET
/api/v1/messages/{id}/export

Parameters

Query Parameters

format*

eml returns the raw RFC 822 bytes as a downloadable file. json returns a JSON representation of the full message.

Type
string
Required
Valid values
"eml""json"

Responses

Exported message. Content-Type is message/rfc822 for eml and application/json for json.

Playground

Variables
Key
Value

Samples


Release

Forward a captured message to a real SMTP server


Forward a captured message to a real SMTP server

POST
/api/v1/messages/{id}/release

Request Body

application/json
JSON
{
  
"host": "smtp.mailgun.org",
  
"port": 587
}

Responses

Message forwarded successfully

Playground

Body

Samples


Authentication

Email authentication header parsing (DKIM, SPF, DMARC, ARC)


Get email authentication results

GET
/api/v1/messages/{id}/auth

Parses authentication-related headers from the raw message and returns structured DKIM, SPF, DMARC, and ARC results. Reads Authentication-Results, DKIM-Signature, Received-SPF, and ARC-Authentication-Results headers. Does not perform cryptographic validation — it displays what upstream mail servers have already verified.

Responses

Parsed authentication results

application/json
JSON
{
  
"dkim": [
  
  
{
  
  
  
"status": "pass",
  
  
  
"details": "dkim=pass header.d=example.com header.s=selector1"
  
  
}
  
],
  
"spf": [
  
  
{
  
  
  
"status": "pass",
  
  
  
"details": "dkim=pass header.d=example.com header.s=selector1"
  
  
}
  
],
  
"dmarc": [
  
  
{
  
  
  
"status": "pass",
  
  
  
"details": "dkim=pass header.d=example.com header.s=selector1"
  
  
}
  
],
  
"arc": [
  
  
{
  
  
  
"status": "pass",
  
  
  
"details": "dkim=pass header.d=example.com header.s=selector1"
  
  
}
  
]
}

Playground

Samples


Assert

CI/CD assertion endpoints — return 200 on pass, 417 on failure


Assert message count matches constraints

GET
/api/v1/assert/count

Returns 200 OK when the number of messages matching the given filters satisfies the min/max bounds. Returns 417 Expectation Failed otherwise. Designed for use in CI pipelines with curl -f. If neither min nor max is provided, the assertion always passes and the response contains the raw count.

Parameters

Query Parameters

min

Minimum number of matching messages (inclusive)

Type
integer
Minimum
0
max

Maximum number of matching messages (inclusive)

Type
integer
Minimum
0
subject

Filter by subject substring (case-insensitive)

Type
string
sender

Filter by sender address substring (case-insensitive)

Type
string
recipient

Filter by recipient address substring (case-insensitive)

Type
string

Responses

Assertion passed

application/json
JSON
{
  
"ok": true,
  
"count": 2
}

Playground

Variables
Key
Value

Samples


WebSocket

Real-time event stream


WebSocket real-time event stream

GET
/api/v1/ws

Upgrade to a WebSocket connection to receive real-time inbox events. The server sends JSON text frames. Clients do not need to send frames (ping/pong is handled at the protocol level).

Event types:

| type | data | |---|---| | message:new | MessageSummary object | | message:delete | { "id": "<ulid>" } | | message:read | { "id": "<ulid>", "is_read": true \| false } | | message:starred | { "id": "<ulid>", "is_starred": true \| false } | | message:tags | { "id": "<ulid>", "tags": ["tag1", "tag2"] } | | messages:clear | (no data field) |

Responses

Switching Protocols — WebSocket handshake successful

Playground

Samples


Powered by VitePress OpenAPI

Released under the MIT / Apache 2.0 License.