Menu
API

Test email verification with Python and JavaScript

Match the right message, bound your polling and keep test credentials out of logs.

Define what a passing email test means

A successful inbox request does not prove that your application sent the right email. Use a fresh test address, record when the test starts, and add a unique marker to the subject in your staging mail template. Match the sender, marker and timestamp together. Then inspect the expected message instead of taking the first item in an inbox.

Run this flow against an application you own or are authorized to test. Temporary inboxes are unsuitable for production account recovery or sensitive customer data. An API key grants API access; knowing a temporary address should not be treated as proof of mailbox ownership.

Prepare the test runner

  1. Save your key as CLEANTEMPMAIL_API_KEY in your CI secret store or local process environment. Do not commit it, put it in a query string, or ship it in a browser bundle.
  2. Create an address with POST /api/generate-email. Optional prefix and domain fields go in a JSON body; send the key in X-API-Key. Read the returned data.email, rather than constructing an address from an old domain list.
  3. Record an integer Unix timestamp immediately before triggering the message in your own application. Put a unique run identifier in its subject. A fresh address per parallel job prevents cross-test collisions.
  4. Poll GET /api/emails?email=…&summary=1. Once a matching ID appears, fetch GET /api/email/{id} only if the assertion needs the body. Summary mode reduces transferred data; each poll is still a request.

Python: bounded polling without extra dependencies

Download wait_for_email.py beside your test. It uses the Python 3.10+ standard library. After your test has triggered its email, call:

import os
import time
import uuid
from wait_for_email import wait_for_email

address = os.environ["TEST_EMAIL_ADDRESS"]  # Generated for this run.
marker = "qa-" + uuid.uuid4().hex
started = int(time.time())
# Trigger your own application's email here, using address and marker.
message = wait_for_email(
    address, os.environ["CLEANTEMPMAIL_API_KEY"],
    "[email protected]", marker, since=started,
    timeout=90, max_attempts=18, interval=5,
)
assert message["id"]

Replace the example sender with the address used by your test application. The trigger is deliberately application-specific: connect it to your signup API, test fixture or browser test. Starting the helper without sending a matching message should time out.

JavaScript: the same contract in Node.js

Download wait-for-email.mjs for Node.js 22+. Import it into your test runner; it is not a frontend script.

import {randomUUID} from 'node:crypto';
import {waitForEmail} from './wait-for-email.mjs';

const address = process.env.TEST_EMAIL_ADDRESS;
const marker = `qa-${randomUUID()}`;
const since = Math.floor(Date.now() / 1000);
// Trigger your own application's email here, using address and marker.
const message = await waitForEmail({
  address, key: process.env.CLEANTEMPMAIL_API_KEY,
  sender: '[email protected]', marker, since,
  timeoutMs: 90000, maxAttempts: 18, intervalMs: 5000,
});
if (!message.id) throw new Error('Expected a matching message');

Assert the body without leaking the verification code

The summary response contains message metadata, not the full HTML or text. Fetch the matching ID for a body assertion. Extract the code using the format of your own template, reject multiple unexpected matches, and submit it only to your test application's expected origin. Do not log an OTP, magic link, complete inbox response or authorization header. Capture status, duration and a synthetic run ID when a test fails.

A six-digit number found anywhere in a message is not automatically the right OTP. For magic links, check the scheme, host and expected path before navigation. Do not open arbitrary links returned by an inbox in your test environment.

Choose a useful failure boundary

These helpers allow at most 18 polls over a 90-second waiting budget, with a per-request timeout. They stop on invalid credentials and confirmed quota exhaustion, and back off on rate limits and temporary server errors. They do not guarantee delivery within 90 seconds. A timeout should produce a controlled test failure, followed by a check of your sender logs and domain availability.

Continue with request budgeting, error handling, or the API reference for generation, message detail and response fields.