API reference
Disposable inboxes over HTTP
Create a throwaway email address, poll it, and read the verification code back already extracted from the message.
The Dev-Mail API is free and open. There is no registration, no API key and no paid tier. A single POST returns an address and a bearer token; every other call uses that token. Reads are idempotent, so an agent can poll the same inbox as often as it needs.
Quickstart
# 1. get an inbox (no auth needed)
curl -sX POST https://dev-mail.com/v1/inboxes
# {"address":"[email protected]","token":"3f9c...","expires_at":"...","ttl_seconds":...}
# 2. wait for the code and read it back
curl -s -H "Authorization: Bearer $TOKEN" \
"https://dev-mail.com/v1/inboxes/$ADDRESS/code?wait=60"
# {"found":true,"code":"309154","from":"Acme","subject":"Confirm your email",...}
That is the whole flow. Step 2 blocks until the code arrives, so you do not need a polling loop of your own.
Wiring this into a test suite instead? The Playwright and Cypress guides carry the same flow as a fixture and a pair of custom commands.
Basics
| Base URL | https://dev-mail.com |
|---|---|
| Auth | Bearer token from POST /v1/inboxes. No signup, no API key. |
| Format | JSON in, JSON out. Standard HTTP status codes. |
| Rate limits | 60 requests per minute per IP; 20 new inboxes per hour per IP. Over the limit returns 429 with Retry-After. |
| Inbox lifetime | 6 hours of inactivity. Every read resets the clock. |
| Direction | Receive only. Dev-Mail cannot send email. |
| Attachments | Not stored. Message text and HTML only. |
| Domains | diyapn.com, mailcom.top, i-os.top, nicepl.com |
| Paid domains | winmail.cfd, kingshould.com, aeria-mall.com. These need a Pro license or a $0.003 per-inbox payment. Each gives out 2 new inboxes an hour across all API callers, which keeps it deliverable. On the website anyone can still pick one by name for free. |
| Machine spec | openapi.json · llms.txt |
Endpoints
GET/v1/domains
List the domains that can receive mail. No auth. domains is what the caller may use right now; send a Pro key as X-License-Key and the reserved ones move into it.
curl -s https://dev-mail.com/v1/domains
{"domains":["diyapn.com", "..."],"count":4,"paid_domains":["winmail.cfd", "..."]}
POST/v1/inboxes
Create a disposable inbox. No auth. Returns the address and the token you use for everything else.
| Body field | Type | Notes |
|---|---|---|
| domain | string | Optional. One of /v1/domains. Picked at random when omitted. Naming one from paid_domains answers 402 with a quote instead. |
| style | string | Optional. american, chinese or japanese — the naming style of the address prefix. Default american. |
curl -sX POST https://dev-mail.com/v1/inboxes \
-H "Content-Type: application/json" \
-d '{"domain":"diyapn.com","style":"american"}'
GET/v1/inboxes/{address}/messages
List every message delivered to the address. Safe to call in a loop: reading does not consume messages or mark them read.
| Query | Type | Notes |
|---|---|---|
| since | integer | Optional unix seconds, inclusive: messages received at or after it. Use the received_ts of the last message you saw. Timestamps are whole seconds, so that message comes back again; skip it by id. |
Each message carries id, from, from_address, to, subject, body, body_text, code, link and received_at / received_ts. Bodies are stored as plain text, so body and body_text hold the same value; raw HTML is not kept, and URLs survive inline.
curl -s -H "Authorization: Bearer $TOKEN" \
"https://dev-mail.com/v1/inboxes/$ADDRESS/messages?since=1757000150"
GET/v1/inboxes/{address}/code
Pull the one-time code out of the newest message that has one. This is the endpoint most callers actually want: it saves you writing the extraction regex yourself.
| Query | Type | Notes |
|---|---|---|
| wait | integer | Optional. Seconds to long-poll while the code is in flight, 0 to 60. Default 0, which returns immediately. |
| since | integer | Optional unix seconds, inclusive: ignores codes from messages received before it. Set it when you ask one inbox for a second code, otherwise the call returns the earlier code straight away instead of waiting for the new mail. Take the timestamp right before you trigger the email; a message landing in that same second still counts. |
Returns found:false with empty fields when no code has arrived before the wait runs out. Codes are matched near words like verification, code, OTP and 验证码, in both the subject and the body; four-digit years are ignored.
curl -s -H "Authorization: Bearer $TOKEN" \
"https://dev-mail.com/v1/inboxes/$ADDRESS/code?wait=60"
Errors
Errors come back as {"error":{"code":"...","message":"..."}} with a matching HTTP status.
| Status | code | Means |
|---|---|---|
| 400 | unknown_domain | The domain is not in the pool. |
| 401 | missing_token | No Authorization header. |
| 401 | invalid_token | The token is not recognised. |
| 403 | not_your_inbox | The address exists but is held by a different token. Create a new inbox. |
| 404 | no_such_inbox | No such address. |
| 429 | rate_limited | Over the IP limit. Honour Retry-After. |
| 503 | domain_at_capacity | The paid domain you named has given out its 2 new inboxes for this hour. Nothing is charged. Honour Retry-After, or name a free domain. |
| 402 | — | Out of inboxes for this hour. Pay per inbox and retry, or wait out Retry-After. See below. |
Out of inboxes? Pay for the next one, no account
A 429 is a dead end for an agent: it has no browser to go buy a plan with, so all it can do is back off, and backing off does not produce the inbox it needs right now. So POST /v1/inboxes answers 402 with a price instead, using the x402 protocol.
$0.003 per inbox, paid in USDC on Polygon (eip155:137). No signup, no API key, no card. Reading, polling and long-polling stay free, so one payment covers a whole verification run rather than each poll.
| Step | What happens |
|---|---|
| 1 | You are over the free quota, so the request returns 402. The quote is in the PAYMENT-REQUIRED header as base64 JSON (x402 v2); the response body repeats it in the v1 shape for older clients. |
| 2 | Your client signs the authorisation offline. Nothing is broadcast yet and no gas is spent by you. |
| 3 | Retry the same request with PAYMENT-SIGNATURE (or X-PAYMENT for v1). The inbox comes back, and the settled transaction is in PAYMENT-RESPONSE. |
Steady heavy use is cheaper on a Pro licence than per inbox. Both work without an account; the licence just raises the free ceiling instead of charging each time.
MCP server for AI agents
Dev-Mail speaks the Model Context Protocol at https://dev-mail.com/mcp over Streamable HTTP. It is stateless and needs no auth, so an assistant can complete a signup flow on its own: create an inbox, watch it, read the code back.
| Tool | Arguments | Returns |
|---|---|---|
| list_domains | — | The receiving domains. |
| create_inbox | domain, style | address + token. |
| list_messages | address, token, since | Every message in the inbox. |
| get_verification_code | address, token, wait | The extracted code. |
Because the server keeps no session, the token from create_inbox is passed back on each later call.
claude mcp add --transport http dev-mail https://dev-mail.com/mcp
Or in an MCP client config file:
{
"mcpServers": {
"dev-mail": {
"type": "http",
"url": "https://dev-mail.com/mcp"
}
}
}
A full signup, end to end
#!/usr/bin/env bash
set -euo pipefail
inbox=$(curl -sX POST https://dev-mail.com/v1/inboxes)
address=$(echo "$inbox" | jq -r .address)
token=$(echo "$inbox" | jq -r .token)
echo "signing up as $address"
# ... trigger the signup on the site under test, using $address ...
result=$(curl -s -H "Authorization: Bearer $token" \
"https://dev-mail.com/v1/inboxes/$address/code?wait=60")
if [ "$(echo "$result" | jq -r .found)" = "true" ]; then
echo "code: $(echo "$result" | jq -r .code)"
else
echo "no code arrived in 60s" >&2; exit 1
fi
Fair use
The API is open because asking for a key would defeat the point. In exchange: stay inside the rate limits, back off when you see a 429, and do not use Dev-Mail to create accounts in bulk or to evade a service's own terms. Inboxes are disposable — nothing here is durable storage, and mail is purged as addresses are recycled.