Getting started
Quickstart
tempy.email gives you disposable inboxes on demand — throwaway mailboxes you
spin up from your test suite or backend to test signup flows, drive E2E suites, and verify the
emails your app actually sends. One HTTP API, one key, and a waitForMessage call that blocks until your email lands — no polling, no flake.
What it's for
- Automate signup / email-verification flows without a real inbox.
- Assert on transactional email in E2E and QA pipelines.
- Wait for a specific message with
waitForMessage(), or receive mail server-to-server over webhooks if you have a public endpoint. - Reply to messages programmatically to test round-trips.
Get an API key
Sign in with GitHub or Google to create an account and issue a key. New accounts start free with 10 credits — creating a mailbox spends 1 credit; reads, waits, replies and deletes are always free.
Fastest path: the TypeScript SDK
The official SDK wraps the API with a typed client, the waitForMessage long-poll,
and helpers like extractCode() / extractLink(). Here's a complete
Playwright test — landing to green in one file:
npm i @tempy/api-client # or: pnpm add @tempy/api-clientimport { test, expect } from '@playwright/test';
import { createTempyClient, extractCode } from '@tempy/api-client';
const tempy = createTempyClient({ apiKey: process.env.TEMPY_KEY! });
test('user can sign up and verify their email', async ({ page }) => {
// a fresh, unique inbox for this test run
const inbox = await tempy.createMailbox();
await page.goto('/signup');
await page.getByLabel('Email').fill(inbox.email);
await page.getByRole('button', { name: 'Create account' }).click();
// block until the verification email lands — or fail after 30s
const msg = await tempy.waitForMessage(inbox.email, {
subjectContains: 'verify',
timeout: 30
});
expect(msg, 'no verification email arrived').not.toBeNull();
// pull the 6-digit code straight out of the email body
await page.getByLabel('Code').fill(extractCode(msg!)!);
await expect(page.getByText('Welcome')).toBeVisible();
});waitForMessage() returns the message as soon as one matches your filter, or null when the timeout elapses — so your test fails deterministically instead
of hanging. Pass since (a timestamp) when you reuse a fixed address, so a stale
message from a previous run can't produce a false positive.
Authentication (plain HTTP)
No SDK? Every endpoint except GET /health requires your key in the X-Api-Key header. The API is server-to-server only — never ship your key to a browser or mobile client.
Proxy through your own backend, exactly like the tempy.email web app does.
# set your key once
export TEMPY_KEY="sk_live_..."
curl https://api.tempy.email/v1/account \
-H "X-Api-Key: $TEMPY_KEY"Base URL is https://api.tempy.email. The legacy alias https://tempy.email/api is kept permanently for older integrations.
curl walkthrough
The full loop with plain curl: create a mailbox, wait for a message, then reply to it.
1. Create a mailbox
curl -X POST https://api.tempy.email/v1/mailbox \
-H "X-Api-Key: $TEMPY_KEY" \
-H "Content-Type: application/json" \
-d '{"local_part": "qa-signup"}' # optional: pick a stable address{
"email": "qa-signup@17mur6.tempy.email",
"web_url": "https://tempy.email/i/17mur6/qa-signup",
"expires_at": "2026-08-29T12:10:00Z",
"seconds_remaining": 600
}Save the email value — that's the {address} path param for
every following call. Point your signup form at it.
2. Wait for the message
Long-poll the mailbox: this call blocks until a message matching your filter arrives, then
returns it. Filters (subject_contains, from, since, unread) are applied server-side. On timeout you get 204 No Content.
# block up to 30s for the verification email, filtered server-side
curl -G https://api.tempy.email/v1/mailbox/$ADDRESS/messages/wait \
-H "X-Api-Key: $TEMPY_KEY" \
--data-urlencode "subject_contains=verify" \
--data-urlencode "timeout=30"
# → 200 with the message, or 204 No Content if the timeout elapses{
"id": "7fae1b57-...",
"from": "no-reply@github.com",
"subject": "Verify your email",
"body_text": "Your code is 558213",
"received_at": "2026-08-29T12:00:03Z"
}Prefer push? Pass webhook_url when you create the mailbox and tempy POSTs each new
message to it as JSON (HMAC-signed). Handy for always-on backends — but in CI, where your
runner has no public URL, wait is the tool. You can also GET /messages (the full list, same filters) if you'd rather pull.
3. Reply to a message
curl -X POST \
https://api.tempy.email/v1/mailbox/$ADDRESS/messages/$MSG_ID/reply \
-H "X-Api-Key: $TEMPY_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Thanks, got the code."}'{ "success": true, "message_id": "0102018f..." }Error handling
Every error returns a stable machine-readable code alongside a human error message, so your tests branch on the reason, not a string. For example no_credits (402, includes a buy link), mailbox_expired vs mailbox_not_found (404 — a too-short TTL is not the same as a bug), address_taken (409), and rate_limited (429). The SDK throws a TempyApiError carrying { status, code, error }.
Next steps
See the full endpoint reference for every route, parameter, and response shape, plus the incoming-message webhook payload.