Receive email via API: a working walkthrough
What you'll build: a working loop that reads a mailbox's *inbound* mail over a single REST endpoint - from the cache for speed, or live from the source in real time - and pages through the results. Unlike a transactional send-API, this is a persistent, owned inbox your agent reads back; there's no webhook to host and no IMAP client to maintain.
Last updated June 30, 2026
The one endpoint you need
Inbound mail is read with a single call:
direction=inbound selects received mail (the default in a mailbox context). Add live=true to force an on-demand IMAP fetch with a cached fallback if the source is briefly unreachable. The response is a Stripe-style list envelope, and each inbound row carries an object discriminator: cached_message (served from our cache) or live_message (a fresh fetch).
Before you start
- A provisioned mailbox (
mbx_…public id) - see Give your agent an email. - A
readorread_writeAPI key (sm_live_…), set asSAIRAPH_MAIL_KEY.
Steps
1. Read inbound mail from the cache (with cURL)
Start with the simplest read. Without live=true you read cached envelopes - fast, and enough for most polling.
Expected response shape:
The list rows are envelopes with a snippet (a short preview), not the full body. To pull a whole message body, fetch it by id (step 5).
2. Read inbound mail from your code
The same call from a plain fetch (no SDK):
…or from Python with requests:
3. Force a live fetch for real-time reads
When you need the very latest - for example an agent waiting on a reply - pass live=true. This does an on-demand IMAP fetch and falls back to the cache if the source is momentarily unreachable (the envelope carries a live_fallback flag when that happens). Live rows arrive with "object": "live_message".
> Tip: poll the cached read on a loop and only escalate to live=true when you actually need the freshest state - it's the cheaper path.
4. Page through results
The list envelope is cursor-paginated. When has_more is true, pass the returned next_cursor back as ?cursor= to get the next page:
Keep following next_cursor until has_more is false.
5. Read one message in full
The list returns previews. To get a full message body, fetch it by its id:
Use the id from a list row (a cmsg_… cached-inbound id). By default this returns the quote-stripped body_text plus a quote_stripped honesty flag; add include=quoted for the unstripped body_text_full, include=html for the raw body_html, or view=raw for the full RFC822 source.
Errors
Errors use a Stripe-shaped envelope. A bad mailbox id, for instance:
Over-limit responses on the agent API are 429 with a Retry-After header - honor it and retry after the stated delay.
Troubleshooting
- Empty `data` array: nothing has arrived yet, or you're reading the cache before the first sync - send the mailbox a test email and re-read with
live=true. - `live=true` is slow: that's expected; it hits the source IMAP server. Prefer cached reads for routine polling.
- `401`/`403`: key missing, expired, or scoped below
read. Re-issue in the dashboard. - Missing body: list rows only carry
snippet. Fetch the message by id (step 5) for the full body.
Next steps
- Read a verification code (OTP) via the API - the real-time read pattern in practice.
- Quickstart: zero to send + receive.
- Full reference: the API docs.
Related
Give your agent a real mailbox
Two-way email over REST and a native MCP server, EU-resident by default.
EU data residencyPer-customer encryptionNative MCP