The "we've emailed you a code" step, solved for agents. Rent a disposable relay mailbox, hand its address to whatever needs to email you, poll for the extracted code.
npm install x402-otp-relay
Half the internet's signup flows end the same way: we've sent a code to your email. An agent with a wallet and no inbox stops dead right there — not because the task is hard, but because there is nowhere for the email to go.
This service is the inbox. Rent a mailbox for a cent, paste its address into the form, poll for the code.
| Route | Price | What you get back |
|---|---|---|
POST /mailbox | $0.01 | Signed { relayAddress, mailboxToken, expiry } |
GET /codes/:token | $0.002 | Codes from mail received so far, ranked with confidence and context |
GET /messages/:token | free | Message metadata, no codes — poll this while waiting |
DELETE /mailbox/:token | free | Release a mailbox early |
POST /verify | free | Signature check on any record |
The naive version — polling /codes once a second for thirty seconds — costs $0.06 and mostly buys empty arrays. The free metadata poll exists precisely so you don't.
A verification email is a haystack full of numbers: order ids, years, prices, phone numbers, tracking codes, footer addresses. "First six digits wins" is wrong often enough to burn login attempts — and a wrong code sometimes locks the account.
{
"code": "481920",
"kind": "numeric",
"confidence": 0.95,
"method": "labelled",
"source": "subject",
"context": "481920 is your verification code"
}
| Tier | Confidence | What it is |
|---|---|---|
| labelled | 0.9–0.95 | Adjacent to "verification code", "one-time passcode", "your PIN" — the sender told us what it is |
| structural | 0.7–0.75 | Standing alone on its own line, or code-shaped in the subject |
| magic link | 0.8 | A URL whose path says verify/confirm/login — returned separately, since following one is a different action from typing a code |
Everything is then filtered against the things that merely look like codes: years, prices ($1234.56), phone numbers, dates, order references, copyright footers, unsubscribe links, and digits that are part of a longer number. HTML-only mail is converted first, because plenty of senders never include a text part.
Mail arrives over actual SMTP, parsed with mailparser — MIME, quoted-printable, base64 and HTML-only all work. Point an MX record at it and real verification email lands here.
relay.example.com. IN MX 10 smtp.example.com.
The address is a hash of the token, not the token:
POST /mailbox → relayAddress: otp-9dc876f944af47e6978c@relay.example.com ← public
mailboxToken: cSrQEfBpVBX1nH2k… ← secret
That distinction matters. The address gets typed into someone else's form and ends up in their logs, their CRM, and probably a support ticket. It must not be the thing that can read the mailbox.
Unknown recipients get a 550, message size is capped at 1 MB, and mailboxes expire — 15 minutes by default, longer than any OTP's own validity and short enough that this isn't a standing pile of other people's verification codes.
What this is not for. The service exists so an agent can complete a verification step for an account it is legitimately setting up or operating. Using a disposable relay to evade a service's identity requirements, create bulk fake accounts, or receive codes for an account that isn't yours is abuse — and in the last case, likely a crime. If you run a deployment, you can see every code that passes through it. Treat that accordingly, and set a short TTL.
| Rail | Network | payTo | Facilitator |
|---|---|---|---|
| EVM | base-sepolia / base | 0x40252CFDF8B20Ed757D61ff157719F33Ec332402 | x402.org |
| Solana | solana / solana-devnet | WwwuGbqHrwF5RG89KhUbmRWEvjnRH9k5kVM5p7T3WwW | PayAI |
Run it, rent a mailbox, deliver real mail over SMTP, read the code.
Every route and every library export, with schemas and error cases.
The cheap loop, reading confidence scores, MCP integration.
The agent-facing contract in one file.
Machine-readable manifest for x402 indexes.
Including the 402 response and PaymentRequirements schema.