Mailflare stored one recipient per message and never used the threading headers: send accepted a single `to`, replies carried no In-Reply-To, and `threadId` was always the message's own Message-ID, so nothing ever grouped. Threading - `resolveThreadId` (src/lib/email/threading.ts) looks up the parent named by In-Reply-To/References among stored messages in the same mailbox and reuses its thread; otherwise the message's own Message-ID seeds a new one. Applied to inbound mail and to .eml/.mbox/IMAP import. - Outbound replies write In-Reply-To and References and file the sent copy under the parent's thread; a fresh send is keyed by the Message-ID Cloudflare returns, which is what any reply will reference. - New `GET /api/messages/[id]/thread` returns the conversation (drafts and trash excluded). The reader shows earlier and later messages as collapsed cards that expand in place; list rows show a "(n)" count. Recipients - `to`, `cc`, `bcc` accept a header string or an array (composer, /api/send, /api/v1/send). Full To and Cc lines are stored on inbound messages so reply-all can address everyone; Bcc is stored on sent mail only. - Composer gains a chip input (comma, Enter, paste, blur commit; Backspace edits the last chip; invalid entries highlighted) with Cc/Bcc toggles. - Reply-all appears only when it would reach someone a plain reply would not, and drops the mailbox's own addresses. - Reader header shows every To/Cc/Bcc recipient; Sent rows read "Name, +2". Schema: migration 0025 adds cc_addr, bcc_addr, in_reply_to, references_header and two indexes; bootstrap SQL and MIGRATION_NAMES updated. mbox export now writes Cc/In-Reply-To/References. Verified against a fresh local D1 with the seeded demo data: a reply sent through /api/send, an .eml reply imported with In-Reply-To pointing at the Cloudflare-assigned Message-ID, and a reply-all from the composer all landed in one 4-message thread. tsc (11) and eslint (96) unchanged from baseline. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PGeWDVSkXtVEmDAN2AtB9h
3.0 KiB
API and integrations
Mailflare exposes APIs for domain management and sending email. Authentication and mailbox permissions still apply to these routes.
Domain management
Adding or removing a domain from Mailflare also updates Cloudflare Email Routing and sending resources.
| Mailflare route | Purpose |
|---|---|
GET /api/domains |
List connected domains |
POST /api/domains |
Connect a domain and configure Cloudflare |
GET /api/domains/[id] |
Get a connected domain |
DELETE /api/domains/[id] |
Remove a domain and clean up its Cloudflare resources |
GET /api/domains/[id]/dns |
View its routing and sending DNS status |
The hostname must be the apex of a zone available to the configured Cloudflare credentials, or a subdomain of that zone. Creating a mailbox also creates the Cloudflare Email Routing rule that delivers its address to the mailflare Worker.
Sending email
Send email through POST /api/v1/send. to, cc and bcc accept either a comma-separated header string or an array of addresses; each entry may carry a display name ("Maya Chen" <maya@example.net>). A message can reach up to 50 recipients across the three fields. Attachments are optional and use Base64-encoded content:
{
"from": "support@example.com",
"to": ["user@example.net", "\"Maya Chen\" <maya@example.net>"],
"cc": "ops@example.com",
"bcc": ["audit@example.com"],
"subject": "Report",
"text": "Attached.",
"attachments": [
{
"filename": "report.pdf",
"type": "application/pdf",
"contentBase64": "<base64 data>"
}
]
}
To send a reply that threads correctly in the recipient's client, pass the parent's Message-ID as inReplyTo and its chain as references (a header string or an array). Mailflare writes the In-Reply-To and References headers, files the sent copy in the same conversation, and stores threadId, inReplyTo and references on every message.
{
"from": "support@example.com",
"to": "user@example.net",
"subject": "Re: Report",
"text": "Thanks, received.",
"inReplyTo": "<CAF1abc@mail.example.net>",
"references": ["<CAF0root@mail.example.net>", "<CAF1abc@mail.example.net>"]
}
GET /api/messages/{id}/thread (session auth) returns every stored message in the same conversation, oldest first, excluding drafts and trash.
The dashboard composer accepts up to 10 attachments, with a 10 MB limit per file and a 20 MB combined limit. Attachment metadata is stored in D1 and file content is stored in R2. Downloads require access to the mailbox containing the message.
Real-time updates
Mailflare uses a Durable Object WebSocket hub to notify connected users after an inbound message is stored. Mailbox owners, the domain administrator, and delegated users receive events for mailboxes they can access.
The REALTIME binding and its migration are declared in wrangler.jsonc. When a WebSocket is temporarily unavailable, the app retries the connection and uses a slower refresh until it recovers.