> Cheers, Billing bot
A real inbox.
Parsed for your agent.
Every mailbox is a real address on a real domain. Read inbound mail as clean JSON, stream new messages over SSE, retrieve over IMAP, or POST signed webhooks. One credential covers every surface.
Start instantly on @myagent.mx · custom domain in minutes · IMAP, SMTP, REST and SSE share one credential
> Cheers, Billing bot
{ "subject": "Re: invoice 4187", "body": { "text": "Got it, paid Tue, thanks.", "signature_stripped": true, "quotes_stripped": true }, "attachments": […] } 200 OK · ready for the agent
The differentiator
What humans see, what your agent reads.
Sendmux strips signatures and quoted history by default, extracts links into a typed array, and returns truncation metadata. Every toggle is exposed if you want different behaviour.
Human view · rendered HTML
Nora, Ops cluster <nora@ops-cluster.dev>
to support-agent@mail.ops-cluster.dev
Subject: Re: invoice 4187
Got it, paid on Tue, thanks.
[tracking pixel · 1×1 gif]
,
Nora · Operations lead · Ops cluster
m: +61 412 555 901
e: nora@ops-cluster.dev
w: ops-cluster.dev
> Hi Nora, can you settle invoice 4187 by EOD?
> Cheers, Billing bot
~9,400 chars · noise dominates the signal
Agent view · clean JSON
{ "id": "msg_q3v9…", "subject": "Re: invoice 4187", "body": { "text": "Got it, paid on Tue, thanks.", "signature_stripped": true, "quotes_stripped": true, "extracted_links": [] }, "attachments": [] }
28 chars of signal · 0 chars of noise
Cleaning controls
Pick what to strip. Pick what to keep.
Same controls on single messages, threads, and batches up to 100. Pass body_mode=clean_json to any content endpoint. Override any default in one query parameter — toggle them below to see the request and the response shift in place.
{ "id": "msg_q3v9pf8z", "thread_id": "thr_7a2k", "subject": "Re: invoice 4187", "body": { "format": "text", "text": "Got it, paid Tue, thanks.", "signature_stripped": true, "quotes_stripped": true, "truncated": false, "max_chars": 100000, "extracted_links": [ "ops-cluster.dev/inv/4187" ] }, "headers": { "From": "…", "Date": "…" }, "attachments": [ { "filename": "invoice.pdf", "size_bytes": 18422 } ] }
Conversations
Threads, grouped by Message-ID.
Every message lands in a thread. Sendmux groups them automatically by Message-ID and References headers — RFC 2822, handled for you. List threads with filters, fetch one for participants and message IDs, or stream the cleaned content.
Auto-grouped, no bookkeeping
Replies and forwards attach to the right thread by header — you never reconcile Message-IDs yourself.
List with filters
Page threads by unread, participant, or date window. Each item carries participants and ordered message IDs.
Fetch cleaned, in order
Pull a single thread and stream each message as clean_json, oldest to newest — ready to feed straight to context.
Read patterns
Sync on your schedule, or stream in real time.
Poll a typed change feed when a loop suits you, or hold an SSE connection open and react the moment mail lands. Same cleaned payloads either way.
Typed change feed
Call /mailbox/changes with a cursor. You get back exactly what moved — new messages, reads, deletions — and a fresh cursor to resume from.
# resume from your last cursor GET /mailbox/changes?cursor=crs_8f3a { "changes": [ { "type": "message.created", "id": "msg_q3v9" }, { "type": "message.read", "id": "msg_p1aa" } ], "next_cursor": "crs_9b21", "has_more": false }
Open SSE connection
Hold /mailbox/stream open and events push as they happen. Auto-reconnect with Last-Event-ID means no gap across a dropped link.
Last-Event-ID after a dropServer-to-server
Signed webhooks you can actually trust.
Every webhook is signed with an HMAC over the raw body. Verify in constant time, check the timestamp, and you know it came from Sendmux — not a spoofer.
Raw-body signature
Sign the exact bytes you received — never the re-serialised JSON.
Timestamp window
Reject anything older than five minutes to kill replays.
Constant-time compare
Use hmac.compare_digest — no timing leaks on the secret.
# POST from Sendmux → your endpointsig = request.headers["X-Sendmux-Signature"]ts = request.headers["X-Sendmux-Timestamp"]# recompute over the RAW bodyexpected = hmac.new(secret, f"{ts}.{raw}", sha256)fresh = now() - int(ts) < 300 # 5 minok = fresh and hmac.compare_digest(sig, expected) → verified · event=message.created · id=msg_q3v9 return 200 OK
Attachments
25 MB in. 25 MB out. No surprises.
Inbound attachments arrive parsed with filename, type, and size. Small files come through inline; large files get a presigned URL so you stream them on your terms.
Where mail lives
Your domain or ours. You pick.
Custom domain provisioning is atomic — partial failure rolls back, so a retry never leaves you half-configured. Sendmux re-verifies every six hours and emails owners the moment a record stops resolving.
No DNS, no setup
Skip the lot with @myagent.mx
Claim a local-part under our shared domain at signup. Globally unique, and outbound rides the managed Amazon SES route automatically.
- Ten seconds to a working mailbox
- Move to your own domain whenever
- Same Mailbox API — REST, SSE, IMAP, SMTP
Operate
Sender rules in front. Suspend, resume, empty behind.
Sender rules run before a message is accepted — three modes: off, allowlist, denylist. Each rule is an allow or deny pattern with an optional note.
Mail rejected by sender rules is not billed — which makes allowlists the cheapest cost-control lever in the system. It runs before any agent code or webhook fires.
Suspend
Cuts authenticate, send, and receive in one call. Resume reverses it.
Empty
Destroys every message in the mailbox. The mailbox itself stays.
Delete
Removes the mailbox and its stored mail together. One atomic step.
One credential
Four surfaces. One password.
REST API
app.sendmux.ai/api/v1/mailbox/*Read, search, send, sync. 35+ endpoints.
SSE stream
GET/mailbox/eventsLive message.received feed. No polling.
IMAP
mail.sendmux.ai:993TLSPlug into anything that speaks IMAP.
SMTP
smtp.sendmux.ai:587STARTTLSSend from the same mailbox address.
Questions