Reading mail
Listing messages
Section titled “Listing messages”GET /inboxes/:id/messages returns mail for one address, newest first:
{ "messages": [ { "id": "msg_91be02c4a7d1", "inboxId": "ibx_k3v9x2mq7a", "receivedAt": "2026-09-18T09:14:03.000Z", "read": false, "sizeBytes": 18432, "folder": "inbox", "starred": false, "content": { "…": "sealed" } } ], "nextCursor": null, "total": 1, "unread": 1}Page with limit (default 50, at most 200) and cursor, passing the previous response’s nextCursor until it is null. total and unread count the whole folder, not the page.
GET /messages/recent returns the account’s newest inbox mail across every address, with limit (default 6, at most 50). GET /inboxes gives each address’s unreadCount.
content is sealed. See Encryption and sealed data.
Mail past its address’s retention window is not returned, and is deleted.
Folders and stars
Section titled “Folders and stars”Every message is in one folder:
| Folder | What is in it |
|---|---|
inbox | Mail that arrived. The default. |
sent | A copy of each message sent from the address, sealed to its key. It carries sentVia: app, api, device, agent, or member for a team member sending from a shared address. |
junk | Mail moved there. |
trash | Mail moved there. |
Pass ?folder= to list one. ?folder=starred lists every starred message outside the trash. Any other value lists the inbox.
POST /messages/:id/starstars a message, and{ "starred": false }unstars it. A star is a flag, not a folder.POST /messages/:id/movewith{ "folder": "junk" },"inbox"or"trash"moves it."restore"takes it out of the trash to the folder it came from. Sent mail can only move to the trash and back.DELETE /messages/:iddeletes one message now.POST /messages/empty-trashdeletes everything in the trash.
Mail in any folder, the trash included, is still deleted when its retention window ends.
One message
Section titled “One message”GET /messages/:id returns a single message in the same shape, as message, whichever folder it is in.
Attachments
Section titled “Attachments”GET /messages/:id/attachments/:index returns one attachment body as sealed bytes, Content-Type: application/octet-stream. The real file name and type are inside the sealed message. Inline images, the ones HTML mail shows with <img src="cid:…">, are attachments too, with a contentId in the sealed message. The apps show them in the body when you tap Show images. Each SDK fetches and opens an attachment for you, for example mc.messages.attachment(message, 0) in TypeScript.
Read state
Section titled “Read state”POST /messages/:id/read marks a message read. Send { "read": false } to mark it unread.
From a paired device
Section titled “From a paired device”A paired device reads its own mail with its device credential: GET /workers/messages, with the same folder parameter. It pages back with before, passing the previous response’s olderCursor until it is null, and gets total and unread for the folder. It can also mark, star, move and delete that mail and fetch its attachments with the endpoints above, and opens all of it with the address keys it was handed at pairing. It cannot reach the account’s other addresses. See Devices.
Reading costs
Section titled “Reading costs”Reading is metered per message body or attachment fetched, not per request. Each plan has an hourly allowance; past it, reads return 429 with Retry-After. See Rate limits.
© Northlab Studios Ltd, trading as Mailcycle. Registered in England and Wales, company no. 17462734. Registered office: 167-169 Great Portland Street, London W1W 5PF.