The HTTP bridge runs alongside the daemon when [bridge] enabled = true
in the active config.toml. It exposes the same IPC contract the TUI uses, but over
HTTP — so the web app, mobile clients, agent runners, and your own shell
scripts all talk to the same daemon through one stable surface.
The bridge serves an OpenAPI 3.1 spec at
http://mxr.localhost:42829/api/v1/openapi.json (port and host configurable
in [bridge]). The spec is authenticated like the rest of the bridge API.
The web app generates its TypeScript client from this spec — you can do the
same for any language with openapi-generator or openapi-typescript.
Every request except /api/v1/health, /api/v1/auth/local-token, and
/api/v1/i18n needs Authorization: Bearer $MXR_TOKEN. OpenAPI and
Swagger UI are not special-cased. WebSocket clients can also pass the token via the
Sec-WebSocket-Protocol subprotocol or as a ?token= query string.
A plain browser location bar cannot attach an Authorization header. For
generated clients, dump /api/v1/openapi.json with curl first. For normal
browser use, open the first-party web app with mxr web.
The SPA served by mxr web doesn’t ask the user to paste a token.
GET /api/v1/auth/local-token is an unauthenticated endpoint that
returns the bridge token to callers whose TCP peer is a loopback IP.
All mutations accept message_ids: string[] in the JSON body unless
noted. They emit a MutationCompleted event over the WebSocket so
clients can reconcile optimistically.
Method
Path
Purpose
POST
/mail/mutations/archive
Remove from inbox
POST
/mail/mutations/trash
Move to trash
POST
/mail/mutations/spam
Mark as spam
POST
/mail/mutations/star
Star / unstar ({starred: bool})
POST
/mail/mutations/read
Mark read / unread ({read: bool})
POST
/mail/mutations/read-and-archive
Combined
POST
/mail/mutations/labels
Modify labels ({add, remove})
POST
/mail/mutations/move
Move to another label
POST
/mail/mutations/undo
Undo via mutation_id
POST
/mail/sync
Trigger sync
POST
/mail/snoozed/{id}/wake
Force-unsnooze
GET
/mail/actions/snooze/presets
Available snooze presets
POST
/mail/actions/snooze
Snooze messages
POST
/mail/actions/unsubscribe
Unsubscribe from list mail
POST
/mail/actions/invite/reply
Dry-run or send a calendar invite RSVP
POST
/mail/deliveries/scan
Backfill scan ({since_days, dry_run})
POST
/mail/deliveries/{id}/resolve
Mark a delivery delivered/done
POST
/mail/deliveries/{id}/dismiss
Hide a false positive
POST
/mail/messages/{message_id}/flags
Set message flags (bitmask in body)
Terminal window
curl-XPOST-H"Authorization: Bearer $MXR_TOKEN"\
-H"Content-Type: application/json"\
-d'{"message_ids":["..."], "starred":true}'\
"$MXR_BASE/api/v1/mail/mutations/star"
List detected calendar invites:
Terminal window
curl-G-H"Authorization: Bearer $MXR_TOKEN"\
"$MXR_BASE/api/v1/mail/invites"\
--data-urlencode'limit=50'
Dry-run calendar replies before sending the iMIP METHOD:REPLY email:
Per-sender aggregates plus recent messages from that sender
GET
/mail/contacts/autocomplete?q=...&limit=...
Prefix-search known senders (filters by email or display name)
The sender response is SenderProfile { profile }. When present, profile
includes recent_messages: the newest messages from that sender with
message_id, thread_id, subject, snippet, date, direction,
and an attachment-present flag. Clients use this to render “Other
emails from sender” and deep-link directly into the matching thread.
The desktop and any other interactive client open a compose session
that the daemon owns. The state (frontmatter + body + attachments)
lives server-side until you send/save/discard.
These are the “platform” features — saved searches, rules, account
management, analytics, LLM status, semantic search. Available even
without an active inbox.
Connect a WebSocket to /api/v1/events and you’ll receive a JSON line
per daemon event. The TypeScript shapes are in
apps/web/src/api/generated.ts under DaemonEvent. Common ones:
MutationCompleted — your last mutation landed (or rolled back)
SyncStarted / SyncFinished
ReminderTriggered — auto-reminder fired
ScheduledSendFlushed — a Send Later draft just went out
IndexBootstrapped — Tantivy completed a startup repair