Skip to content

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:

Code

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 read or read_write API key (sm_live_…), set as SAIRAPH_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.

cURL

Expected response shape:

JSON

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):

JavaScript (fetch)

…or from Python with requests:

Python

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:

Code

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:

Code

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:

JSON

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

  • REST API

    The HTTP API reference.

    Learn more
  • Read a verification code (OTP) via API

    A step-by-step walkthrough.

    Learn more
  • Quickstart: from zero to send + receive in 10 minutes

    A step-by-step walkthrough.

    Learn more
  • Product

    What Sairaph Mail does.

    Learn more

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