Skip to content

Read a verification code (OTP) via API

What you'll build: a short blocking loop that waits for a one-time code (OTP / 2FA) to land in your agent's mailbox and pulls the code out - fetched live from the source so the agent isn't blocked by cache lag. This is the canonical "confirm a signup" / "pass a 2FA gate" flow for an agent that owns a real inbox.

Last updated June 30, 2026

How it works in one paragraph

OTP emails are time-sensitive, so you read them with live=true - an on-demand fetch straight from the source rather than the cache. You poll the inbound list until a matching message appears, then read the code from the message (often visible in the snippet preview; pull the full body by id when you need certainty). A read-scoped key is enough - the agent never needs to send.

Before you start

  • A provisioned mailbox (mbx_…) whose address you'll use as the signup/login email.
  • A read-scoped sm_live_… key, set as SAIRAPH_MAIL_KEY.
Shell

Steps

1. Trigger the email

Have your agent (or the system under test) submit the mailbox's address to whatever sends the code - a signup form, a login 2FA step, a CI sign-in. The code is now in flight to your mailbox.

2. Poll the inbox, live

Read inbound mail with live=true so you get the freshest state rather than a cached snapshot:

cURL

This returns the Stripe-style list envelope. Inbound rows arrive as live_message (fresh) or cached_message, each with from_addr, subject, and a snippet preview. Loop this call on a short interval (e.g. every 2-3 seconds) with a sensible timeout (e.g. 60 seconds), filtering by the expected from_addr or subject until the code's message appears.

3. Extract the code

Most providers put the code in the subject or the first line, so the snippet preview usually contains it - match it with a regex such as \b\d{6}\b (adjust for your code length/format). When you need the complete body to be certain, fetch the message by id:

Code

Use the id from the matched list row (a cmsg_… id). The default response returns the quote-stripped body_text; include=html adds the raw body_html if the code is only in an HTML part. Then run your regex over the body.

A Python sketch of the poll loop

The read call itself comes from the SSOT (READ_INBOUND_PY reads direction=inbound&live=true); wrap it in a timed loop and a regex:

Python
Python

Troubleshooting

  • Code never arrives: confirm you submitted the mailbox's exact address; check the right from_addr; widen the timeout. Re-read with live=true (a cached read can lag).
  • Code is in the body, not the snippet: fetch the message by id and add include=html if the code is HTML-only.
  • Polling too aggressively: back off to 2-3 second intervals; live=true hits the source server each time.
  • Multiple codes / stale code: sort by received_at and take the newest matching message so you never use an expired code.
  • `429`: you're over the rate limit - honor the Retry-After header before the next poll.

Next steps

  • Otp Verification Codes

    Learn more.

    Learn more
  • Receive email via API: a working walkthrough

    A step-by-step walkthrough.

    Learn more
  • REST API

    The HTTP API reference.

    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