Automated Testing in CI/CD
Run end-to-end authentication and transaction tests with Playwright, Cypress, or Vitest using the Emailens wait API.
Testing transactional emails, two-factor authentication (2FA), and magic links in CI/CD pipelines often suffers from timing flakiness. The Emailens Sandbox provides a long-polling wait API that blocks until a matching email arrives, returning extracted tokens and URLs ready for automation.
Polling Endpoint: /api/sandbox/v1/messages/wait
Instead of polling repeatedly in a tight loop with sleep(), your test runner sends a single request that stays open until a matching email is ingested.
Request Syntax
GET https://app.emailens.dev/api/sandbox/v1/messages/wait?inboxId={INBOX_ID}&timeout=30&subject=Verify HTTP/1.1
Authorization: Bearer ek_live_...Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
inboxId | string | Yes | Target sandbox inbox ID |
timeout | number | No | Maximum wait duration in seconds (default 30, max 60) |
subject | string | No | Optional case-insensitive substring filter on email subject |
to | string | No | Optional recipient filter |
Response Payload
When the message arrives, Emailens returns HTTP 200 with parsed fields:
{
"message": {
"id": "msg_98127391",
"inboxId": "inbox_clx9abc",
"subject": "Your verification code: 492019",
"from": "auth@myproduct.com",
"to": "test-runner@try.emailens.dev",
"otp": "492019",
"actionUrl": "https://myproduct.com/verify?token=eyJh...",
"receivedAt": "2026-10-08T12:04:12Z"
}
}Playwright End-to-End Example
Here is a complete Playwright test verifying a user signup flow with a 6-digit OTP code:
import { test, expect } from "@playwright/test";
test("user sign-up with email OTP verification", async ({ page }) => {
const testInboxId = process.env.EMAILENS_INBOX_ID!;
const apiKey = process.env.EMAILENS_API_KEY!;
const testEmail = "test-user@try.emailens.dev";
// 1. Fill sign-up form
await page.goto("https://myapp.com/signup");
await page.fill('input[name="email"]', testEmail);
await page.click('button[type="submit"]');
// 2. Wait for confirmation input screen
await expect(page.locator('input[name="otp"]')).toBeVisible();
// 3. Wait for the email in the Emailens sandbox
const waitResponse = await fetch(
`https://app.emailens.dev/api/sandbox/v1/messages/wait?inboxId=${testInboxId}&timeout=30&subject=verification`,
{
headers: {
Authorization: `Bearer ${apiKey}`,
},
}
);
expect(waitResponse.ok).toBeTruthy();
const { message } = await waitResponse.json();
expect(message.otp).toBeDefined();
// 4. Fill OTP into page and verify dashboard redirects
await page.fill('input[name="otp"]', message.otp);
await page.click('button[type="submit"]');
await expect(page).toHaveURL("https://myapp.com/dashboard");
});Vitest / Node.js Magic Link Example
Testing passwordless magic links in API integration tests:
import { test, expect } from "vitest";
test("request magic link and verify token session", async () => {
const inboxId = process.env.EMAILENS_INBOX_ID!;
const apiKey = process.env.EMAILENS_API_KEY!;
// Trigger magic link dispatch
await fetch("https://myapp.com/api/auth/magic-link", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: "tester@try.emailens.dev" }),
});
// Long poll Emailens
const response = await fetch(
`https://app.emailens.dev/api/sandbox/v1/messages/wait?inboxId=${inboxId}&timeout=20`,
{
headers: { Authorization: `Bearer ${apiKey}` },
}
);
const { message } = await response.json();
expect(message.actionUrl).toContain("https://myapp.com/auth/confirm?token=");
// Follow the magic link
const authResponse = await fetch(message.actionUrl, { redirect: "manual" });
expect(authResponse.status).toBe(302);
});SMTP Credentials & Rotation
Provision per-service credentials, enforce STARTTLS transit encryption, and rotate credentials with zero downtime.
AI Agent Integration (MCP)
Allow AI coding agents (Claude, Cursor, Windsurf) to inspect sandbox inboxes and run end-to-end authentication tests via Model Context Protocol.