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 URLhttps://dev-mail.com
AuthBearer token from POST /v1/inboxes. No signup, no API key.
FormatJSON in, JSON out. Standard HTTP status codes.
Rate limits60 requests per minute per IP; 20 new inboxes per hour per IP. Over the limit returns 429 with Retry-After.
Inbox lifetime6 hours of inactivity. Every read resets the clock.
DirectionReceive only. Dev-Mail cannot send email.
AttachmentsNot stored. Message text and HTML only.
Domainsdiyapn.com, mailcom.top, i-os.top, nicepl.com
Paid domainswinmail.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 specopenapi.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 fieldTypeNotes
domainstringOptional. One of /v1/domains. Picked at random when omitted. Naming one from paid_domains answers 402 with a quote instead.
stylestringOptional. 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.

QueryTypeNotes
sinceintegerOptional 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.

QueryTypeNotes
waitintegerOptional. Seconds to long-poll while the code is in flight, 0 to 60. Default 0, which returns immediately.
sinceintegerOptional 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.

StatuscodeMeans
400unknown_domainThe domain is not in the pool.
401missing_tokenNo Authorization header.
401invalid_tokenThe token is not recognised.
403not_your_inboxThe address exists but is held by a different token. Create a new inbox.
404no_such_inboxNo such address.
429rate_limitedOver the IP limit. Honour Retry-After.
503domain_at_capacityThe 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.
402Out 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.

StepWhat happens
1You 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.
2Your client signs the authorisation offline. Nothing is broadcast yet and no gas is spent by you.
3Retry 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.

ToolArgumentsReturns
list_domainsThe receiving domains.
create_inboxdomain, styleaddress + token.
list_messagesaddress, token, sinceEvery message in the inbox.
get_verification_codeaddress, token, waitThe 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.