Create a throwaway email, catch the verification mail, extract the code in three HTTP calls. No IMAP, no browser automation, no SDK install. Ships with an MCP server for agents.
# Dispatch a disposable inbox $ curl -X POST api.mailsink.dev/v1/inboxes # → signup-k8m2@codenotify.net # ...your app or agent signs up with that address... # Wait for the verification mail, get the code $ curl api.mailsink.dev/v1/inboxes/inb_rae1z/wait-for-code # → { "code": "847291", "from": "stripe.com" }
// Dispatch a disposable inbox const inbox = await fetch('/v1/inboxes', { method: 'POST' }) .then(r => r.json()); // → inbox.address: signup-k8m2@codenotify.net // ...your app or agent signs up with that address... // Wait for the verification mail, get the code const { code } = await fetch( `/v1/inboxes/${inbox.id}/wait-for-code`, ).then(r => r.json()); // → code: "847291"
# Dispatch a disposable inbox inbox = requests.post('/v1/inboxes').json() # → inbox['address']: signup-k8m2@codenotify.net # ...your app or agent signs up with that address... # Wait for the verification mail, get the code code = requests.get( f'/v1/inboxes/{inbox["id"]}/wait-for-code', ).json()['code'] # → "847291"
Standard REST with a bearer token. No SDK. No OAuth flow. No IMAP credentials. Works from a CI runner, a Playwright suite, or an agent runtime. Anything that can make an HTTP request.
POST /v1/inboxes returns a live email address, an id, and a TTL. Use a shared domain from the free tier, or your own domain on Pro.
POST /v1/inboxes { "local_part": "signup", "ttl": 3600 } // → 201 { "id": "inb_rae1z", "address": signup-k8m2@codenotify.net }
Feed it into the signup form, the password reset, the test harness,
wherever a real email would go. Mail arrives in milliseconds. No
polling required; the wait_for_email endpoint blocks
until one lands.
// Playwright await page.fill( '#email', 'signup-k8m2@codenotify.net' ); await page.click('#submit'); // The mail is already on its way.
GET /v1/inboxes/:id/latest-code parses the message and returns the OTP or magic link as JSON. No regex on your end. Basic extraction, with honest limits documented per sender.
GET /v1/inboxes/inb_rae1z/latest-code // → 200 { "code": "847291", "from": "noreply@stripe.com", "subject": "Your verification code" }
MailSink ships an MCP server as an npm package. Once installed, your agent has native tools for creating inboxes, waiting on mail, and returning codes, same as any other tool.
Works in Claude Code, Cursor, Windsurf, and any other runtime that speaks MCP. Configuration is a one-line entry in your agent config.
No wrapper scripts. No SDK versioning. No brittle browser automation to drive a signup.
| Tool | Purpose |
|---|---|
| create_inbox | Provision a throwaway address and return its id. |
| wait_for_email | Block up to timeout seconds until a message arrives. |
| get_verification_code | Return the OTP from the latest message. |
| get_verification_link | Return the magic link from the latest message. |
| list_messages | Enumerate everything the inbox has received. |
| delete_inbox | Dispose of the inbox before its TTL. |
// Spin up a disposable browser session const browser = await chromium.launch(); const page = await browser.newPage(); await page.goto('https://temp-mail.xyz'); const addr = await page.textContent('.addr'); // Run the signup, poll until mail lands await signupFlow(addr); for (let i = 0; i < 30; i++) { if (await page.$('.inbox-msg')) break; await page.reload(); await sleep(1000); } // Scrape the body, regex the OTP const body = await page.textContent('.body'); const otp = body.match(/\d{6}/)?.[0];
// Your agent calls two typed tools create_inbox({ ttl: 3600 }) // → signup-k8m2@codenotify.net // ...agent performs the signup... get_verification_code({ inbox_id, timeout: 30 }) // → { code: "847291" }
Provisioning mailboxes, rotating credentials, parsing MIME, keeping the domain off blocklists. That's a full-time job. We've done the job. You get the API.
Services for persistent agent identities charge for storage, organisation, and long-term threading. For test signups and OTP flows you want the opposite shape: arrive, extract, vanish.
The public disposable-email sites are built for humans clicking through a UI. No programmatic access, shared domains that get blocked by the hour, and TOS that ban automation.
Mailtrap is optimised for developers inspecting message content during local QA. MailSink is optimised for CI runners and agents that need to act on the mail, not stare at it.
Every inbox lives on one of our shared domains. When a blocklist kicks in on one, new inboxes auto-provision on a clean one and the affected domain rotates out until it clears. You don't manage DNS, you don't babysit reputation.
MailSink runs on Cloudflare Workers with D1 for metadata and R2 for storage. There's no region to pick, no cold starts, and no servers to nurse. The API responds where your CI or your agent already is.
Start on the free tier while you're wiring it up. Move to Pro when shared domains slow you down. Month-to-month, no seat pricing, no hidden usage cliffs.
For wiring it up and personal projects.
For real test suites and agent runs.
For CI farms, agencies, and agent platforms.
GET /v1/messages/:id.wait_for_email), which covers the common agent case. Webhooks are on the roadmap once we see how teams actually use the API.GitHub sign-in. Ten seconds. No credit card. Your first inbox is live before the kettle boils.