DEV Community

Avinash Verma
Avinash Verma

Posted on

I built a free disposable-email API on Cloudflare Workers (no key, no signup)

Testing email flows is tedious. Every time I needed to verify a sign-up, a password reset, or a confirmation email in a test, I reached for a disposable-email service — and every one with a usable API wanted an account, an API key, or a paid tier. I wanted something I could curl in one line. So I built one: TempMailPortal, a free temporary-email tool with a no-key, CORS-enabled JSON API. Here's how it works and how it's built.

The API in 30 seconds

Create an inbox — no auth required:

curl -X POST https://api.tempmailportal.com/api/inbox
# -> {"address":"g9j4pdiyx9@ynoxa.com","token":"…"}
Enter fullscreen mode Exit fullscreen mode

Poll it (the token is the only credential):

const API = "https://api.tempmailportal.com";
const { address, token } = await (await fetch(`${API}/api/inbox`, { method: "POST" })).json();

const timer = setInterval(async () => {
  const auth = { headers: { Authorization: `Bearer ${token}` } };
  const list = await (await fetch(`${API}/api/messages`, auth)).json();
  if (list.length) {
    clearInterval(timer);
    const msg = await (await fetch(`${API}/api/messages/${list[0].id}`, auth)).json();
    console.log(msg.subject, msg.text);
  }
}, 5000);
Enter fullscreen mode Exit fullscreen mode

Full docs: https://tempmailportal.com/api.

The architecture — it's all on Cloudflare's edge

There's no origin server. One Cloudflare Worker does three jobs:

  1. fetch() — the HTTP API the frontend (and you) call.
  2. email() — the inbound-mail handler. A Cloudflare Email Routing catch-all on the mailbox domain forwards every message to the Worker, which parses it with postal-mime and writes it to D1 (Cloudflare's SQLite).
  3. scheduled() — a cron that deletes messages after ~24h.

That's the whole backend. D1 for storage, Email Routing for ingestion, Workers for compute, a cron for cleanup. It scales to zero and costs almost nothing.

Two things that turned out to matter

Stateless tokens instead of a sessions table. When you create an inbox you get a bearer token that's just the mailbox name signed with HMAC (base64url(mailbox).base64url(hmac)). The Worker verifies the signature on each request — no lookup table, no session store. The address itself is public and guessable, so the token isn't a real secret; it just scopes reads/deletes to one mailbox and keeps the API stateless.

Never put mailboxes on your brand domain. This is the one I'd warn anyone building this about. The website and API live on the brand domain; the actual @… addresses are issued on a separate, cheap, rotatable "mailbox" domain. If you hand out disposable addresses on your main domain, it lands on disposable-email blocklists and wrecks your deliverability and SEO. Keep them strictly separated.

Honest limitations

  • Receive-only. It can't send mail (by design — that keeps it useless for spam).
  • Public addresses. Anyone who guesses the local-part sees the inbox; never use it for anything sensitive.
  • Auto-expiry. Messages disappear after ~24h.
  • One mailbox domain right now, rotated over time.

If you test email flows, automate QA, or just want a throwaway inbox you can script, it's free and there's nothing to sign up for. I'd love feedback on the API design — what's missing for your use case? (Webhooks and a JS SDK are the two I hear most.)

Top comments (0)