DropMail.me API
Programmatically create disposable email addresses, receive messages in real time, and read their contents — no sign-up, no spam, no strings attached. Ideal for automated testing, onboarding flows, and AI agents that need a live inbox.
Free tokens, no account needed
Flexible queries & subscriptions
WebSocket & long-poll push
Works with AI assistants
- HTTP endpoint
https://dropmail.me/api/graphql/${AUTH_TOKEN}- WebSocket endpoint
wss://dropmail.me/api/graphql/${AUTH_TOKEN}/websocket- Format
- GraphQL · schema.graphql ↓
- Auth
- Free
af_…token as${AUTH_TOKEN}— no sign-up required. Get yours ↓
HTTP & WebSocket transport details
HTTP endpoint expects standard GraphQL parameters: query, variables, operationName and extensions. Only query is mandatory.
Both GET and POST methods could be used. For GET just add URL-encoded parameters as query string: /api/graphql/MY_TOKEN?query=…&variables=… (variables should be a JSON object). POST accepts application/json and application/x-www-form-urlencoded; if no Content-Type header is provided, the body is assumed to be JSON. See detailed specification.
Response format is application/json by default, or streamed multipart/mixed when you send Accept: multipart/mixed (useful for subscriptions). See detailed specification.
WebSocket endpoint supports both graphql-ws and Apollo protocols (negotiated via Sec-WebSocket-Protocol). Consult your GraphQL client library for details.
Want to use DropMail from an AI assistant? There is an MCP server package that exposes DropMail as tools — no GraphQL knowledge needed.
❯ Sign me up for the free trial at example.com and give me the confirmation code
● create_session → tmpbox47@dropmail.me
● wait_for_email → 📬 "Verify your email address" from promo@example.com arrived
● read_email → Your code: 482-951
Learn more about the MCP package ↓
Get a free API token
Free tokens (af_…) let you use the API without a sign-up. No account required.
${AUTH_TOKEN} is deprecated. Please generate a free af_ token below instead. Legacy tokens will be rejected starting April 2026. In the meantime, rate limits for legacy tokens are much stricter than for af_ tokens.
Renewing preserves your token's identity — any resources you created with the old token (e.g. personal domains) remain accessible with the new one.
News
- 2026-03-08
- Rotating domains — some domains now have a known DNS expiry date (
Domain.expiresAt). Within 30 days of expiry they are excluded from random selection but still usable when chosen explicitly; after expiry the domain is gone entirely. PasspermanentDomainOnly: truein mutations or usedomains(permanentOnly: true)to restrict to permanent domains. Rotating domains tend to be freshly registered and less likely to be spam-blocked — good for short-lived use cases. - 2026-02-21
- Free API tokens (
af_…) introduced — no sign-up required. Using arbitrary strings as tokens is now deprecated and will stop working in April 2026. - 2026-02-20
- MCP server package released — use DropMail directly from AI assistants like Claude Desktop, VS Code Copilot, and GitHub Copilot CLI. Available on PyPI.
- 2026-02-12
- Introducing gentle rate-limits. No worries, they are quite high (hundreds per-minute).
- 2025-05-29
- You can add your own domains to DropMail with personal domains. In case public DropMail domains are banned
- 2023-11-28
- More efficient way to get notified about new incoming mail - subscriptions
- 2023-10-24
- Now
Session.mailsandSession.mailsAfterIdonly returns up to 100 mails. To get more useSession.mailsConnection - 2022-02-12
- WebSocket transport added
Object model
- Domain
- Domain name, like
@dropmail.meor@10mail.orgfor which you can crate emailAddresses. Some domains are only available via API, seeDomain.availableVia. You may also add your own domains (you'd need to set-up DNS records and calladdDomainmutation). - Session
- Is a server-side storage unit. It stores
Addresses and incomingMails. It has limited lifetime - when session expires all itsAddresses andMails are removed from the server. You can create multipleSessions and attach multipleAddresses to eachSession. By default session's lifetime is 10min and extended each time you access it (by making any query that reads or writes some data to it). - Address
- Email address, like
user@dropmail.me,wasd@10mail.org. You can only chooseDomainof the address, but "login" part (the one that comes before@) is assigned by the server automatically and is always unique and belongs to you only. It's always attached to one of theSessions and it is able to receive mail as long as the Session it's attached to is not expired. If you think you may need to use this address in the future, you should save itsrestoreKeyand use it later withrestoreAddressoperation. - Incoming email that arrived to one of the
Addresses and is stored inside theSession. When Session expires, email is removed from our server completely and forever. You can request different parts of the email (like it's plaintext,headerSubject,attachmentsetc) or get wholerawMIME mail ordownloadUrlto download the raw mail by HTTP. - Attachment
- It's only defined when received
Mailhas any attachments and we were able to parse it. They can be accessed viaMail.attachmentsfield.
Workflow
Creating a Session with single random Address
To create a new sessions, use introduceSession mutation.
Here we create a new Session with new random Address and asking the server to return session's id, expiration time and new Addresses email address
mutation {
introduceSession {
id,
expiresAt,
addresses {
address
}
}
}
the output of this query would look like this:
{
"data": {
"introduceSession": {
"id": "U2Vzc2lvbjqcMxamadJC_aLiPz_XL0lK",
"expiresAt": "2021-02-18T04:20:00+00:00",
"addresses": [
{
"address": "example@10mail.org"
}
]
}
}
}
From now on, you can send mail to example@10mail.org and server would store it inside the Session with ID "U2Vzc2lvbjqcMxamadJC_aLiPz_XL0lK".
"expiresAt": "2021-02-18T04:20:00+00:00" means that this session would self-destruct at this moment (it's always in the future time), unless something happens with it (some query reads from it, or new email received). Each time session is accessed, its expiresAt is extended. So, in theory, it may stay alive forever.
Fetching the incoming mail
To check for the incoming mail, you have two options
- Periodical polling - periodically (eg, once every 10 second) inspect the contents of the Session using
session(id)orsessionsquery (easier, less efficient) - Subscriptions - to use
sessionMailReceivedsubscription (advanced, more efficient)
Periodical polling (basic)
Here we are requesting all (up to 100) mails for a specific session.
query {
session(id: "U2Vzc2lvbjqcMxamadJC_aLiPz_XL0lK") {
mails{
rawSize,
fromAddr,
toAddr,
downloadUrl,
text,
headerSubject
}
}
}
This query would return an empty array when there are no mails:
{
"data": {
"session": {
"mails": []
}
}
}
And would return a Mail object for each received email, when there are some:
{
"data": {
"session": {
"mails": [
{
"toAddr": "example@10mail.org",
"text": "Hello\r\n",
"rawSize": 812,
"headerSubject": "Hello",
"fromAddr": "test@example.com",
"downloadUrl": "https://dropmail.me/download/mail/gql:1:9c3316a6-69d2-42fd-a2e2-3f3fd72f494a/vb18co6tn6b4pv10hgr7lhaljcnrhvk5"
}
]
}
}
}
If session has already expired, null will be returned instead.
{
"data": {
"session": null
},
"errors": [
{
"path": ["session"],
"message": "session_not_found",
"extensions": {
"code": "SESSION_NOT_FOUND"
}
}
]
}
When you plan to receive multiple emails with the same session, it's recommended to use mailsAfterId(mailId: ID) field. When no mailId provided, it returns the same results as mails. But when some existing mailId is specified, it would only return mails, received after this mailId.
So, when you start the session, first you query mailsAfterId without mailId (or just mails), asking it to return Mail.id as well. When you recieve your first mail, you'll call mailsAfterId with received mail's id as a parameter, so, only new - unread mail will be returned.
Subscriptions (advanced)
The benefit of subscriptions is that you don't have to keep sending requests periodically to see if there is a new mail, but the server notifies you immediately when there is a new mail. To use subscriptions you need to rely on more advanced techniques. There are 3 alternatives:
- Subscription via HTTP long-polling
- Subscription via HTTP multipart/mixed streaming
- Subscription via WebSocket
subscription {
sessionMailReceived(id: "U2Vzc2lvbjqcMxamadJC_aLiPz_XL0lK") {
rawSize,
fromAddr,
toAddr,
downloadUrl,
text,
headerSubject
}
}
To use this command with long-polling method you run it the same way as with any other command, however the server will not reply immediately, but will block until mail is received (up to 3 minutes). It can only return one mail at a time with this method and it always returns the first received mail by default. To get the next one, you need to add mailsAfterId parameter (works the same way as Query.mailsAfterId).
To use it with multipart/mixed streaming, you need to add Accept: multipart/mixed HTTP request header. In that case the received emails will be streamed as multipart/mixed document that contains application/json bodies per-email. It means you would need a MIME parser to extract the data.
To use it with WebSocket (recommended) you need to use a GraphQL client library that supports WebSocket transport. Please consult your library's documentation for details.
Custom addresses
You can create a new session without Address or with one initial Address (that has random or specific Domain). And you can always add more Addresses (with random or specific Domain) to the Session later.
You can't choose the login/username part of the address! It's always unique and generated by the server.
Anyway, first you would need to read the list of all the available domains and their IDs (IDs never change, so you can cache them locally; we add new ones from time to time):
query {
domains {
id,
name,
availableVia,
expiresAt
}
}
Result:
{
"data": {
"domains": [
{
"name": "dropmail.me",
"id": "RG9tYWluOjE=",
"availableVia": ["API", "TELEGRAM", "WEB"],
"expiresAt": null
},
{
"name": "10mail.org",
"id": "RG9tYWluOjI=",
"availableVia": ["API", "TELEGRAM", "WEB"],
"expiresAt": "2028-06-01T00:00:00+00:00"
},
<..>
]
}
}
Then you may create a new session with specific domain:
mutation {
introduceSession(input: {withAddress: true,
domainId: "RG9tYWluOjE="}) {
id,
addresses {
address
}
}
}
Or create an empty session and add more addresses later:
mutation {
introduceSession(input: {withAddress: false}) {
id
}
}
mutation {
introduceAddress(input: {sessionId: "U2Vzc2lvbjqcMxamadJC_aLiPz_XL0lK"}) {
address,
restoreKey
}
}
mutation {
introduceAddress(input: {sessionId: "U2Vzc2lvbjqcMxamadJC_aLiPz_XL0lK",
domainId: "RG9tYWluOjE="}) {
address,
restoreKey
}
}
Some domains are rotating — they have a Domain.expiresAt and will stop working at some point. If you need a long-lived address, either pass permanentDomainOnly: true to introduceSession or introduceAddress to get a permanent domain, or query domains with expiresAt and pick one with a far enough expiry date:
mutation {
introduceSession(input: {withAddress: true,
permanentDomainOnly: true}) {
id,
addresses {
address,
domain { name, expiresAt }
}
}
}
Likewise, domains(permanentOnly: true) returns only permanent domains when you want to present a filtered list to the user.
Personal domains
In case public dropmail domains don't work for you (eg, they all are banned), you may register your own domain with DropMail and use it the same way you use any other DropMail domain.
${AUTH_TOKEN}! If you loose your ${AUTH_TOKEN}, you won't be able to use and manage your domain!
To use your own domain with DropMail, you should do 2 steps:
- Set-up MX DNS records on your domain
- Call
addDomainmutation
For DNS there are 2 options: the easiest one is to add @ MX 10 mx.dropmail.me and * MX 10 mx.dropmail.me records to your domain (mx.10mail.org is also allowed).
For example, if your domain is example.com, then MX records for example.com as well as any subdomain of example.com should point to mx.dropmail.me.
Such that nslookup -type=MX example.com as well as nslookup -type=MX blabla.example.com should report mx.dropmail.me.
Another option is that MX records for your domain and subdomains point to whatever domain that resolves to the same IPv4 (required) and IPv6 (optional) as mx.dropmail.me. We don't change MX IPs often, however it's going to be your responsibility to monitor the change - we won't send any notifications!
When DNS is ready, you should call addDomain mutation. This mutation validates that your domain has correct DNS setup and if it succeeds, it returns the Domain object. If DNS validation fails, you need to correct the reported error and try again, however you may have to wait for DNS TTL to expire.
This mutation requires that you provide some password that you should remember - it would be needed if you decide to unregister the domain with deleteDomain.
mutation {
addDomain(input: {name: "example.com",
adminPassword: "my-secret-password"}) {
id,
name,
availableVia
}
}
Result:
{
"data": {
"addDomain": {
"id": "RG9tYWluOjX=",
"name": "example.com",
"availableVia": ["API"]
}
}
}
After that you may use this domain ID with introduceAddress(input: {domainId: "RG9tYWluOjX=", sessionId: "..."}). It would be visible in domains query.
And also introduceAddress() and introduceSession() mutations with random domain would randomly draw your personal domain along with public ones.
To unregister your personal domain, make sure you use the same ${AUTH_TOKEN} and adminPassword and call deleteDomain mutation.
mutation {
deleteDomain(input: {id: "RG9tYWluOjX=",
adminPassword: "my-secret-password"})
}
Errors
HTTP 403 — authentication errors
These are returned before any GraphQL is evaluated, as a plain 403 Forbidden response with the reason string in the body.
token_expired- Your
af_…token has passed its expiry date. Generate a new one. - any other reason
- Token is missing, too short, or has an invalid signature. Double-check that you're passing the correct token in the URL.
GraphQL errors
Returned inside the standard errors[] array. Each error has an extensions.code field:
Always check whether the response contains an errors field and handle it before processing data. Some errors (e.g. SESSION_NOT_FOUND) are permanent — retrying the same request will not help.
SESSION_NOT_FOUND- The session has expired or never existed. Sessions live for at least 10 minutes, extended on every access. Once gone, the session and all its mail are deleted permanently — create a new session instead of retrying. If you saved the
restoreKeyfor any of the addresses, you can restore them into a new session using therestoreAddressmutation. RATE_LIMIT_EXCEEDED- Request quota exceeded. The error always includes an
extensions.captchafield — interactive clients may show a captcha challenge; automated scripts should simply slow down. See Rate Limiting below for details. LEGACY_TOKEN_DEPRECATED- Your token is a legacy anonymous token. It will stop working in April 2026 — please generate a free
af_token instead. This error is non-blocking:datais still present in the response. It appears on every response (including subscription events) as a reminder. Legacy tokens also have stricter rate limits thanaf_tokens. resolver_error- Catch-all for other resolver errors. The
messagefield contains a machine-readable reason. Common values from personal-domain mutations:unknown_domain,domain_not_found,invalid_domain_name,password_too_short,malformed_password.
Rate Limiting
To protect the service from abuse, the API implements rate limiting on address creation and mail reception.
When rate limits are exceeded, address creation mutations will return an error, and mail access (queries/subscriptions) will return null with an error. Active subscriptions will be terminated with an error notification.
Example error response:
{
"data": {
"session": {
"id": "U2Vzc2lvbjphYmNkZWY=",
"mails": null
}
},
"errors": [{
"message": "Request quota exceeded",
"path": ["session", "mails"],
"extensions": {
"code": "RATE_LIMIT_EXCEEDED",
"captcha": {…}
}
}]
}
What to do: Simply slow down your requests. Rate limits are based on sliding time windows, so adding reasonable delays between operations (e.g., 1-2 seconds) will prevent issues. The API is designed for programmatic access with significantly more generous limits than you would experience accessing the website directly, but please use it responsibly.
Note: For interactive clients the extensions.captcha field contains a captcha challenge — solving it grants extra quota credits.
Try it from your browser!
Here you can try to query our API right from your browser.
(generate a free token or paste your own af_… token)
af_… token above.List domains
Introduce session
This request would return a new email address as data.introduceSession.addresses[0].address, you can try to send emails to it!
What is your ? (data.introduceSession.id):
List all sessions
You can see all your non-expired sessions, even when you forgot to save the Session ID.
Add one more address to the session
You can have multiple addresses attached to the session.
Query a specific session
Call this API periodically to fetch incoming mail.
Subscribe to new session mail
Use subscriptions to let server notify you about new mail.
Try your own query
Here you can edit your own query and run it.
Code examples
Here we are using CURL command line utility to query the API.
Creating a new session with random email address
$ curl --silent -H "Content-Type: application/x-www-form-urlencoded" https://dropmail.me/api/graphql/MY_TOKEN -d 'query=mutation {introduceSession {id, expiresAt, addresses{id, address}}}' | jq
{
"data": {
"introduceSession": {
"id": "U2Vzc2lvbjqcMxamadJC_aLiPz_XL0lK",
"expiresAt": "2021-02-18T01:40:02+00:00",
"addresses": [
{
"id": "QWRkcmVzczqcMxamadJC_aLiPz_XL0lKOmRyb2ZAMTBtYWlsLm9yZw",
"address": "example@10mail.org"
}
]
}
}
}
Now "example@10mail.org" is active and it can receive emails.
Fetching received emails
Use periodic polling or subscriptions to receive incoming mail.
$ curl --silent https://dropmail.me/api/graphql/MY_TOKEN -d 'query=query ($id: ID!) {session(id:$id) {id, expiresAt, mails{id, rawSize, raw, fromAddr, toAddr, receivedAt, downloadUrl, toAddrOrig, decodeStatus, text, headerFrom, headerSubject, html}, addresses{id, address, restoreKey} }}&variables={"id": "U2Vzc2lvbjqcMxamadJC_aLiPz_XL0lK"}' | jq
{
"data": {
"session": {
"mails": [
{
"toAddrOrig": "example@10mail.org",
"toAddr": "example@10mail.org",
"text": "Hello\r\n",
"receivedAt": "2021-02-18T01:30:17+00:00",
"rawSize": 812,
"raw": "MIME-Version: 1.0\r\nMessage-ID: <test@mail.example.com>\r\nSubject: Hello\r\nFrom: Hello <test@example.com>\r\nTo: aonfmqcpw@example.com\r\nContent-Type: text/plain; charset=US-ASCII\r\n\r\nHello\r\n",
"id": "TWFpbDqcMxamadJC/aLiPz/XL0lKOnZiMThjbzZ0bjZiNHB2MTBoZ3I3bGhhbGpjbnJodms1",
"html": null,
"headerSubject": "Hello",
"headerFrom": "Hello <test@example.com>",
"fromAddr": "test@example.com",
"downloadUrl": "https://dropmail.me/download/mail/gql:1:9c3316a6-69d2-42fd-a2e2-3f3fd72f494a/vb18co6tn6b4pv10hgr7lhaljcnrhvk5",
"decodeStatus": "OK"
}
],
"id": "U2Vzc2lvbjqcMxamadJC_aLiPz_XL0lK",
"expiresAt": "2021-02-18T01:41:50+00:00",
"addresses": [
{
"restoreKey": "0c88ea831f8f71bf3885f2b5bddd6c371",
"id": "QWRkcmVzczqcMxamadJC_aLiPz_XL0lKOmRyb2ZAMTBtYWlsLm9yZw",
"address": "example@10mail.org"
}
]
}
}
}
Same, but more compact:
$ curl --silent https://dropmail.me/api/graphql/MY_TOKEN -d 'query=query ($id: ID!) {session(id:$id) {mails{rawSize, fromAddr, toAddr, downloadUrl, text, headerSubject}} }&variables={"id": "U2Vzc2lvbjqcMxamadJC_aLiPz_XL0lK"}' | jq
{
"data": {
"session": {
"mails": [
{
"toAddr": "example@10mail.org",
"text": "Hello\r\n",
"rawSize": 812,
"headerSubject": "Hello",
"fromAddr": "test@example.com",
"downloadUrl": "https://dropmail.me/download/mail/gql:1:9c3316a6-69d2-42fd-a2e2-3f3fd72f494a/vb18co6tn6b4pv10hgr7lhaljcnrhvk5"
}
]
}
}
}
MCP (AI agents)
MCP (Model Context Protocol) lets AI assistants
call external tools. The dropmail-mcp package wraps this GraphQL API so agents can
create temporary addresses and receive mail without writing any GraphQL themselves.
Example session
Things you can ask your AI assistant:
- "Give me a temp email and wait for the confirmation from example.com"
- "Sign me up for the newsletter at example.com using a throwaway address"
- "I need to sign up for a product trial on example.com — give me a .com temp email, wait for their email, and pull out the activation code"
- "Check if the password reset email has arrived and give me the link"
❯ Give me a temp email — I need to sign up for a free trial
◐ I'll create a temporary address with DropMail.
● create_session
└ {"address":"tmpbox47@dropmail.me","restore_key":"a1b2c3…"}
Your temporary address: tmpbox47@dropmail.me
Save the restore key (a1b2c3…) if you want to recover it later.
❯ Done, I submitted the form — please wait for the confirmation email
● wait_for_email
└ {"arrived":true,"headerSubject":"Verify your email address","fromAddr":"noreply@example.com",…}
📬 Email arrived!
From: Example Service <noreply@example.com>
Subject: Verify your email address
Preview: Please verify your email address to complete your registration. Use the…
❯ What's the code?
● read_email
└ {"text":"…Your verification code is 482-951. It expires in 10 minutes…","textSource":"HTML",…}
Your verification code is: 482-951
Install with pip or pipx, then run:
pip install dropmail-mcp # or: pipx install dropmail-mcp
dropmail-mcp --token YOUR_TOKEN
You can also use uvx dropmail-mcp --token YOUR_TOKEN for a quick terminal test without installing, though uvx may not work reliably when launched as a subprocess by MCP clients — use pip/pipx for the configs below.
Configure your AI assistant
Add to claude_desktop_config.json:
{
"mcpServers": {
"dropmail": {
"command": "dropmail-mcp",
"env": {
"DROPMAIL_TOKEN": "YOUR_TOKEN"
}
}
}
}
Add to .vscode/mcp.json in your workspace (VS Code 1.99+, agent mode):
{
"servers": {
"dropmail": {
"type": "stdio",
"command": "dropmail-mcp",
"env": {
"DROPMAIL_TOKEN": "${input:dropmail-token}"
}
}
},
"inputs": [
{
"id": "dropmail-token",
"type": "promptString",
"description": "DropMail API token (obtain at dropmail.me/api/)",
"password": true
}
]
}
VS Code will prompt for the token securely — it is never stored in plain text.
Edit ~/.copilot/mcp-config.json (create if it doesn't exist):
{
"mcpServers": {
"dropmail": {
"command": "dropmail-mcp",
"env": {
"DROPMAIL_TOKEN": "${DROPMAIL_TOKEN}"
}
}
}
}
Set DROPMAIL_TOKEN in your shell profile (~/.bashrc,
~/.zshrc, etc.), or run /mcp add inside the Copilot CLI
session to configure it interactively.
Available tools
| Tool | What it does |
|---|---|
list_domains | List available @domain options |
create_session | Create a session and get a temporary address |
get_session | Check session state, addresses, and mail count |
get_inbox | List received emails with subject and text preview |
wait_for_email | Block until a new email arrives (up to 180 s) |
read_email | Read full email: text body, attachments, download URLs |
add_address | Add a second address to an existing session |
restore_address | Restore an old address using its restore key |
delete_address | Remove an address from a session |
Download
Available on PyPI — the recommended way to install:
pip install dropmail-mcp
Pre-built packages are also available for offline or air-gapped installs:
- Wheel:
dropmail_mcp-latest.whl - Source:
dropmail_mcp-latest.tar.gz
pip install https://dropmail.me/static/dist/dropmail_mcp-latest.whl