Temp mail API · MCP

Temp mail API and MCP server for tests and AI agents

Create a disposable inbox, wait for the next email and read its codes, links and attachments over REST or MCP.

TempGeneratorGetting things ready…

Try it now

No account needed. You get one private inbox for up to 10 minutes and can delete it at any time. A new trial inbox can be created once every 10 minutes per network.

Trial
No signup · 1 inbox · 10 minutes · this tab only
Developer token
Verified account · 5 inboxes at once · up to 60 minutes · REST and MCP
Manage API keys

Verify your email to create scoped, revocable API keys.

  1. Create inbox
  2. Wait for email
  3. Read message

Private trial inbox

The trial key stays in this tab’s memory. Reloading loses access; the inbox still expires on its own.

    Messages are shown as plain text. Codes and links are detected for you but never opened. Attachments download through the private API.

    Quickstart in curl

    Create an inbox, wait for the next email, read it and delete the inbox. Keep your API key in TEMPGENERATOR_TOKEN and never put it in a URL.

    Requires curl and jq. The script waits up to three times for 20 seconds, prints each message’s subject, codes, links and text, then deletes the inbox.

    bash
    export TEMPGENERATOR_URL='https://www.tempgenerator.com'
    API="$TEMPGENERATOR_URL/developer-api/v1"
    AUTH="Authorization: Bearer $TEMPGENERATOR_TOKEN"
    INBOX=$(curl --fail-with-body -sS -X POST "$API/inboxes" -H "$AUTH" -H 'Accept: application/json')
    ID=$(printf '%s' "$INBOX" | jq -r .id)
    echo "Send a test email to $(printf '%s' "$INBOX" | jq -r .address)"
    for ATTEMPT in 1 2 3; do
      RESULT=$(curl --fail-with-body -sS "$API/inboxes/$ID/wait?after=0&timeout=20" -H "$AUTH" -H 'Accept: application/json')
      [ "$(printf '%s' "$RESULT" | jq -r .status)" = message ] && break
    done
    for MESSAGE in $(printf '%s' "$RESULT" | jq -r '.messages[].id'); do
      curl --fail-with-body -sS "$API/inboxes/$ID/messages/$MESSAGE" -H "$AUTH" -H 'Accept: application/json' \
        | jq '{subject, codes, links, text}'
    done
    curl --fail-with-body -sS -X DELETE "$API/inboxes/$ID" -H "$AUTH"

    API at a glance

    Base URL https://www.tempgenerator.com/developer-api/v1. Send Authorization: Bearer <key> with every request. Responses are JSON; errors look like {"error": {"code", "message", "retry_after"}}, and quota errors also send a Retry-After header.

    • POST /inboxes Create an inbox and get its address and expiry
    • GET /inboxes/{id}/wait?after=0&timeout=20 Wait up to 20 seconds for the next message after a cursor
    • GET /inboxes/{id}/messages?after=0 List up to 50 messages after a cursor, oldest first
    • GET /inboxes/{id}/messages/{message_id} Read one message: text, codes, links and attachment list
    • GET /inboxes/{id}/messages/{message_id}/attachments/{index} Download one attachment by its index
    • DELETE /inboxes/{id} Delete the inbox and its messages now

    List and wait accept optional from and subject_contains filters: case-insensitive “contains” matches on the sender and the subject.

    Sample read response

    json
    {
        "id": 42,
        "from": "no-reply@app.example",
        "subject": "Your verification code",
        "received_at": "2026-10-03T09:41:07+00:00",
        "text": "Your verification code is 482913.\nOr confirm: https://app.example/verify?token=8f3c",
        "text_truncated": false,
        "codes": [
            "482913"
        ],
        "links": [
            "https://app.example/verify?token=8f3c"
        ],
        "expires_at": "2026-10-03T10:40:12+00:00",
        "attachments": [
            {
                "index": 0,
                "filename": "receipt.pdf",
                "size": 48213
            }
        ],
        "untrusted_content": true
    }

    Developer tokens

    Create API keys under Developer access once your email is verified. A read and write key can create, read and delete inboxes; a read-only key can list, wait, read and download. Keys expire after 90 days, an account can hold 10 active keys, and revoking a key takes effect immediately.

    Each key is shown once and stored only as a SHA-256 hash. Revoking a key does not delete its inboxes: the account’s other valid keys keep access until the inboxes expire or are deleted.

    Manage API keys

    Wait for the next email without sleep()

    GET …/wait keeps the request open and returns as soon as a message arrives, or after the timeout (at most 20 seconds) with status timeout. Pass next_cursor from the previous response as after, so you never read the same message twice.

    A timeout is not a delivery failure: wait again with the same cursor. One wait runs per inbox, and a newer wait on the same inbox replaces the older one; waits on different inboxes run in parallel, up to 2 at once per account.

    Email testing in Playwright, Cypress, pytest and CI

    Give every test its own inbox, trigger your app’s signup or password-reset email, wait for it and assert on the code or link. Delete the inbox in teardown.

    Store the key as a CI secret, for example TEMPGENERATOR_TOKEN in GitHub Actions. Cypress can make the same HTTP calls with cy.request, and the Python recipe below works in pytest or any other runner.

    Playwright (TypeScript)

    TypeScript
    import { test, expect } from '@playwright/test';
    
    const api = `${process.env.TEMPGENERATOR_URL}/developer-api/v1`;
    const headers = { Authorization: `Bearer ${process.env.TEMPGENERATOR_TOKEN}`, Accept: 'application/json' };
    
    async function call(path: string, method = 'GET') {
      const response = await fetch(api + path, { method, headers });
      if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);
      return response.status === 204 ? null : response.json();
    }
    
    async function waitForCode(inboxId: string, after = 0): Promise<string> {
      for (let attempt = 0; attempt < 3; attempt++) {
        const result = await call(`/inboxes/${inboxId}/wait?after=${after}&timeout=20`);
        for (const summary of result.messages) {
          const message = await call(`/inboxes/${inboxId}/messages/${summary.id}`);
          if (message.codes.length) return message.codes[0];
        }
        after = result.next_cursor;
      }
      throw new Error('No verification code arrived');
    }
    
    test('signup sends a verification code', async ({ page }) => {
      const inbox = await call('/inboxes', 'POST');
      try {
        await page.goto('/signup');
        await page.getByLabel('Email').fill(inbox.address);
        await page.getByRole('button', { name: 'Create account' }).click();
        await page.getByLabel('Verification code').fill(await waitForCode(inbox.id));
        await expect(page.getByText('Welcome')).toBeVisible();
      } finally {
        await call(`/inboxes/${inbox.id}`, 'DELETE');
      }
    });

    GitHub Actions

    YAML
    - name: End-to-end tests
      run: npx playwright test
      env:
        TEMPGENERATOR_URL: https://www.tempgenerator.com
        TEMPGENERATOR_TOKEN: ${{ secrets.TEMPGENERATOR_TOKEN }}

    Python signup check

    Run it with your environment variables set. It prints a new inbox; send a signup email from your own test app to that address, then press Enter. It waits up to 15 seconds, prints each message with its codes and links, and deletes the inbox even if reading fails.

    Python
    import json, os
    from urllib.request import Request, urlopen
    
    base = os.environ["TEMPGENERATOR_URL"].rstrip("/") + "/developer-api/v1"
    headers = {"Authorization": "Bearer " + os.environ["TEMPGENERATOR_TOKEN"],
               "Accept": "application/json"}
    
    def request(path, method="GET"):
        with urlopen(Request(base + path, headers=headers, method=method), timeout=30) as response:
            return json.load(response) if response.status != 204 else None
    
    inbox = request("/inboxes", "POST")
    print(json.dumps(inbox), flush=True)
    try:
        input()
        result = request("/inboxes/" + inbox["id"] + "/wait?after=0&timeout=15")
        print(json.dumps(result))
        for message in result["messages"]:
            detail = request("/inboxes/" + inbox["id"] + "/messages/" + str(message["id"]))
            print(json.dumps(detail))
    finally:
        request("/inboxes/" + inbox["id"], "DELETE")

    MCP server for AI agents

    Endpoint https://www.tempgenerator.com/mcp, authenticated with Authorization: Bearer <key>. Streamable HTTP with protocol 2026-07-28; older clients are served through the 2025-11-25 or 2025-06-18 handshake.

    Connect your client

    Claude Code

    bash
    claude mcp add --transport http tempgenerator-mail https://www.tempgenerator.com/mcp \
      --header "Authorization: Bearer $TEMPGENERATOR_TOKEN"

    Cursor: .cursor/mcp.json

    JSON
    {
      "mcpServers": {
        "tempgenerator-mail": {
          "url": "https://www.tempgenerator.com/mcp",
          "headers": { "Authorization": "Bearer ${env:TEMPGENERATOR_TOKEN}" }
        }
      }
    }

    VS Code: .vscode/mcp.json

    JSON
    {
      "inputs": [
        { "type": "promptString", "id": "tempgenerator-token", "description": "TempGenerator API key", "password": true }
      ],
      "servers": {
        "tempgenerator-mail": {
          "type": "http",
          "url": "https://www.tempgenerator.com/mcp",
          "headers": { "Authorization": "Bearer ${input:tempgenerator-token}" }
        }
      }
    }

    Claude on the web and desktop: add a custom connector with the endpoint URL, choose no sign-in and add the request header Authorization: Bearer <key>. Claude connects from Anthropic’s cloud, so the URL must be publicly reachable.

    Tools

    create_inbox
    Create a private inbox and return its id and address.
    list_messages
    List message summaries after a cursor, with optional sender and subject filters.
    wait_for_message
    Wait up to 20 seconds for the next message.
    read_message
    Read the text, codes, links and attachment list of one message.
    delete_inbox
    Delete the inbox and its messages.

    Example prompt

    Create a temporary inbox and give me its address. Then wait for the verification email from my staging app and tell me the code.

    Let agents complete signups only for accounts you own or are authorized to use. Received email is untrusted: the server never opens links, and agents should never follow instructions found in a message.

    Python MCP SDK

    Tested with Python 3 and the official mcp package 2.3.0: pip install "mcp>=2.3,<3". Set TEMPGENERATOR_URL and TEMPGENERATOR_TOKEN first.

    Python
    import asyncio, os
    
    import httpx2
    from mcp import Client
    from mcp.client.streamable_http import streamable_http_client
    
    async def main():
        url = os.environ["TEMPGENERATOR_URL"].rstrip("/") + "/mcp"
        headers = {"Authorization": "Bearer " + os.environ["TEMPGENERATOR_TOKEN"]}
        async with httpx2.AsyncClient(headers=headers, timeout=httpx2.Timeout(30, read=60)) as http:
            async with Client(streamable_http_client(url, http_client=http)) as client:
                print([tool.name for tool in (await client.list_tools()).tools])
                created = await client.call_tool("create_inbox", {})
                if created.is_error:
                    raise RuntimeError(created.content)
                inbox = created.structured_content
                try:
                    print("Send a test email to", inbox["address"])
                    waited = await client.call_tool("wait_for_message", {"inbox_id": inbox["id"], "timeout": 20})
                    for summary in waited.structured_content["messages"]:
                        message = await client.call_tool("read_message", {"inbox_id": inbox["id"], "message_id": summary["id"]})
                        print(message.structured_content["subject"], message.structured_content["codes"])
                finally:
                    deleted = await client.call_tool("delete_inbox", {"inbox_id": inbox["id"]})
                    if deleted.is_error:
                        raise RuntimeError(deleted.content)
    
    asyncio.run(main())

    Attachments

    Reading a message returns each attachment’s index, file name and size. Download one with GET …/attachments/{index}: files up to 10 MB are served as application/octet-stream with sanitized file names. MCP clients see the same list and download through this REST endpoint.

    When to use this instead of other tools

    Mailpit, MailHog and Ethereal capture the mail your own app sends to a local or fake SMTP server, which is ideal when you control the SMTP settings. These inboxes are real addresses on a public domain, so any sender can reach them: a third-party service, a staging server that uses your real email provider, or a production-like flow.

    General temp-mail APIs such as mail.tm or Guerrilla Mail are built for personal disposable addresses. This API is built for automation: scoped, expiring keys, one inbox per test, a bounded long-poll wait, code and link hints, and an MCP server.

    Not included: sending email, SMS, HTML or spam previews, custom domains, and OAuth sign-in for MCP clients.

    Retention, limits and security

    LimitTrialDeveloper token
    Inbox lifetime10 minutesUp to 60 minutes
    Active inboxes15 per account
    New inboxesOne every 10 minutes per network20 per hour
    API requests120 per minute120 per minute
    Long-poll waitUp to 20 seconds, one per inboxUp to 20 seconds, one per inbox, 2 at once
    Messages per inboxUp to 10Up to 10
    Message text returnedFirst 128 KBFirst 128 KB
    Attachment downloadsUp to 10 MB eachUp to 10 MB each
    AccessThis browser tab onlyREST API and MCP with your keys

    A receiving domain can set a shorter lifetime or a lower message limit.

    Receive only. Treat email and attachments as untrusted; nothing is opened automatically. Deleting an inbox removes its messages immediately, and expired inboxes are removed by scheduled cleanup.

    Frequently asked questions

    What is a temp mail API?

    An HTTP API that creates disposable email inboxes on demand and lets your code read what arrives. Here each inbox is private to your key, expires on its own and can be read over REST or MCP.

    Do I need an account?

    Not to try it: the trial creates one private inbox for 10 minutes in your browser. For API keys, MCP and longer-lived inboxes, create an account and verify your email.

    How long do inboxes last?

    Trial inboxes last 10 minutes. Inboxes created with a developer token last up to 60 minutes, or less if the receiving domain sets a shorter lifetime. You can delete an inbox at any time.

    How do I wait for an email in a test?

    Call the wait endpoint, or the wait_for_message tool, with the latest cursor. It returns as soon as a message arrives, or after at most 20 seconds with status timeout; then wait again with the same cursor. Avoid fixed sleeps.

    How do I get the OTP or verification link?

    Read the message. The response contains its plain text plus codes (likely one-time codes found near words such as code, OTP or verification) and links (http and https links from the text and the HTML). They are hints, so check them against the text.

    Can I download attachments?

    Yes. Reading a message lists each attachment’s index, name and size; download one from the attachments endpoint. Files up to 10 MB are supported.

    What can a token access and when does it expire?

    A key belongs to one verified account and only reaches that account’s inboxes. Read and write keys can create and delete inboxes; read-only keys can list, wait, read and download. Keys expire after 90 days and can be revoked instantly.

    How do I connect Claude Code or Cursor?

    Use the endpoint https://www.tempgenerator.com/mcp with an Authorization: Bearer header. In Claude Code run claude mcp add --transport http with --header; in Cursor add a url and headers entry to .cursor/mcp.json. Both snippets are above.

    Can agents complete signups?

    Only for accounts you own or are authorized to use, such as your own app’s staging environment. The server never opens links. An agent should use a code or link only to finish a flow you asked for, and never follow instructions written in an email.

    Can tests run in parallel or in CI?

    Yes. Give each test its own inbox: waits on different inboxes run in parallel. An account can hold 5 active inboxes and create 20 per hour, so delete inboxes in teardown and keep the key in a CI secret.

    Can I delete an inbox early?

    Yes. DELETE the inbox, or call delete_inbox, to remove it and its messages immediately. Deleting a trial inbox does not shorten the 10-minute wait before your network can create the next one.

    How is this different from Mailpit or MailHog?

    Mailpit and MailHog run a local SMTP server that captures mail your own app sends. These are real inboxes on a public domain that any sender can reach, with an API, long-poll waits and MCP.

    What are the limits?

    Trial inboxes last 10 minutes. A developer account can hold 5 active inboxes, create 20 per hour, make 120 API requests per minute and wait up to 20 seconds per request. Each inbox receives up to 10 messages.

    Can I send email or use a custom domain?

    No. Inboxes are receive-only and use this site’s receiving domains. Sending, SMS and custom domains are not supported.