MailToYou

API Documentation

A simple REST + SSE API for creating temporary mailboxes, receiving verification emails, and getting real-time notifications. No signup or API key required.

Quick start in 60 seconds

The fastest way to understand the API is to copy these three curl commands and run them one by one. You will get a working mailbox and see it receive a test email.

  • Step 1 — Create a mailbox: curl -X POST https://api.m2u.io/v1/mailboxes/auto
  • Step 2 — From the response, note down the token and view_token. You need BOTH to read messages.
  • Step 3 — List messages: curl "https://api.m2u.io/v1/mailboxes/TOKEN/messages?view=VIEW_TOKEN"
  • Step 4 — Send a test email to LOCAL_PART@DOMAIN (also from the response), then list messages again. You will see it.

How the API is shaped

The entire API is built around one object: the mailbox. When you create a mailbox you get back two secrets. The token identifies which mailbox it is. The view_token proves you are allowed to read messages from it. Both are required on every read request — this double-token design prevents anyone who sees the email address from also seeing the inbox contents.

  • No signup, no API key, no OAuth — just call the endpoints
  • Rate limits apply per IP fingerprint (not per account)
  • All endpoints return JSON unless stated otherwise
  • Errors always follow the shape { error: "error_code", reason?: "details" }

Base URL

All examples in this page use https://api.m2u.io as the base URL. If you self-host MailToYou, replace it with your own API_BASE_URL.

1. GET /v1/domains — list available domains

Returns the domains you can use when creating a custom mailbox. Call this first if you want to let your users pick a domain.

  • Auth: none
  • curl: curl https://api.m2u.io/v1/domains
  • Response: { "domains": ["cpu.edu.kg", "do4.tech", "tmail.bio", ...] }

2. POST /v1/mailboxes/auto — create a random mailbox

Creates a mailbox with a random local part on a random domain. This is the endpoint most scripts should use. No Turnstile required.

  • Auth: none
  • Request body (optional): { "token": "existing_token" } — pass an existing token to get the same mailbox back (useful for polling)
  • curl: curl -X POST https://api.m2u.io/v1/mailboxes/auto -H 'Content-Type: application/json' -d '{}'
  • Response: { "mailbox": { "token", "view_token", "local_part", "domain", "expires_at" } }
  • Your email address = `${local_part}@${domain}`
  • Save token AND view_token — you need both to read messages later

3. POST /v1/mailboxes/custom — create a custom-named mailbox

Creates a mailbox with a chosen prefix like `myname@cpu.edu.kg`. Because local parts are a shared resource, this endpoint requires a Cloudflare Turnstile token (solved by a user in a browser). Scripts cannot use this endpoint directly.

  • Auth: Turnstile token (browser-only)
  • Request body: { "localPart": "myname", "domain": "cpu.edu.kg", "turnstileToken": "..." }
  • Response: same shape as /auto
  • Errors: address_taken (someone else is using it), invalid_domain (not in /v1/domains), turnstile_failed, daily_limit_exceeded

4. GET /v1/mailboxes/:token/messages?view=VIEW_TOKEN — list inbox

Lists up to the 50 most recent messages for a mailbox, newest first. Only subject and metadata — no body. For polling-based workflows.

  • Auth: token (in path) + view_token (in ?view query param) — both required
  • curl: curl "https://api.m2u.io/v1/mailboxes/TOKEN/messages?view=VIEW_TOKEN"
  • Response: { "messages": [{ "id", "from_addr", "subject", "received_at" }] }
  • Empty inbox returns { "messages": [] } (not an error)

5. GET /v1/mailboxes/:token/messages/:id?view=VIEW_TOKEN — read one message

Retrieves the full body of a single email, including sanitized HTML and plain text.

  • Auth: same dual-token as the list endpoint
  • curl: curl "https://api.m2u.io/v1/mailboxes/TOKEN/messages/MESSAGE_ID?view=VIEW_TOKEN"
  • Response: { "message": { "id", "from_addr", "subject", "text_body", "html_body", "received_at" } }
  • html_body is sanitized — scripts, iframes, and tracking pixels are stripped

6. GET /v1/mailboxes/:token/stream?view=VIEW_TOKEN — real-time push (SSE)

Server-Sent Events endpoint. Open a long-lived connection and you get pushed notifications when new mail arrives — no polling needed. This is how the website shows emails in real time.

  • Auth: same dual-token as reading
  • Event: connected — fires on successful connection, payload { "ok": true }
  • Event: mail — fires when new mail arrives, payload { "id", "from", "subject", "receivedAt" }
  • Keep-alive comment sent every 20 seconds so proxies don't time out
  • Max 5 concurrent SSE connections per mailbox — 6th connection closes the oldest

End-to-end example (Node.js)

Here is a complete polling example you can drop into any Node script. It creates a mailbox, polls for 2 minutes, and prints every message that arrives.

  • const API = 'https://api.m2u.io';
  • const res = await fetch(`${API}/v1/mailboxes/auto`, { method: 'POST' });
  • const { mailbox } = await res.json();
  • console.log(`Send mail to ${mailbox.local_part}@${mailbox.domain}`);
  • const url = `${API}/v1/mailboxes/${mailbox.token}/messages?view=${mailbox.view_token}`;
  • for (let i = 0; i < 60; i++) {
  • const { messages } = await (await fetch(url)).json();
  • if (messages.length) { console.log(messages); break; }
  • await new Promise(r => setTimeout(r, 2000));
  • }

Rate limits

Rate limits apply per IP fingerprint (IP + User-Agent + Accept-Language). They are intentionally generous — real users will never hit them. If your script sees { "error": "rate_limited" }, back off for 60 seconds and retry.

  • POST /v1/mailboxes/auto — 60 per minute, 200 per day
  • POST /v1/mailboxes/custom — 10 per minute, 10 per day (plus Turnstile, so scripts can't use this)
  • GET /v1/mailboxes/:token/messages — 100 per minute
  • GET /v1/mailboxes/:token/messages/:id — 100 per minute
  • SSE stream — 5 concurrent connections per mailbox

Error reference

Every error response has the shape { "error": "code", "reason"?: "detail" }. The HTTP status is 200 for business errors (rate_limited, daily_limit_exceeded, address_taken, etc.) and 4xx/5xx only for protocol errors. Always check response.error before using response.mailbox or response.messages.

  • rate_limited — you hit the per-minute limit, wait 60s
  • daily_limit_exceeded — you hit the per-day limit, wait until tomorrow or use /auto instead of /custom
  • address_taken — the custom local_part is already in use, pick another
  • invalid_domain — the domain is not in /v1/domains
  • turnstile_failed — the Turnstile token is missing, expired, or invalid
  • access_denied — the token or view_token is wrong, or the mailbox expired
  • no_domains — server has no active domains (contact operator)
  • validation_failed — request body does not match the schema, details in the response

Data retention

Mailboxes live for 7 days from creation. Individual emails live for 24 hours from arrival. A cleanup job runs every 30 minutes and permanently deletes everything past its expiration — there is no recovery, not even for the operator. Design your scripts to extract what they need from the response body immediately, not rely on long-term storage on our side.

Is there a paid tier?

Not yet. MailToYou is currently free and anonymous. If you would find a paid tier useful (longer retention, webhooks, custom domain, higher limits for automation), open an issue on GitHub or reach out — we build what users actually ask for.

API Documentation | MailToYou