Mailloop provides a TypeScript SDK that lets you create sandboxes, send emails through them, and verify delivery programmatically. This is designed for automated test suites and CI/CD pipelines.
Getting an API Key
Go to your dashboard, open Settings > API Keys, and create a new key. Keys use the format ml_live_.... Store it as an environment variable:
export MAILLOOP_API_KEY=ml_live_your_api_key_here SDK Installation
# npm
npm install @mailloop/sdk
# yarn
yarn add @mailloop/sdk
# pnpm
pnpm add @mailloop/sdk Requires Node.js 18 or later. Zero dependencies.
Basic Pattern
The typical automated email test follows this flow:
- Create a temporary sandbox
- Configure your application to send through it
- Trigger the action that sends email
- Wait for the email to arrive
- Assert on the email content
import { MailloopClient } from '@mailloop/sdk';
const client = new MailloopClient({
apiKey: process.env.MAILLOOP_API_KEY
});
// 1. Create a temporary sandbox (auto-deletes after 60 seconds)
const sandbox = await client.sandboxes.createTemporary({
duration: 60
});
// 2-3. Your application sends email using sandbox.username / sandbox.password
// as SMTP credentials against sandbox.mailloop.io
// 4. Wait for the email
const email = await client.emails.waitFor(sandbox.id, {
subject: 'Welcome',
timeout: 30000
});
// 5. Assert
console.log(email.subject); // "Welcome to Acme"
console.log(email.html); // Full HTML body
console.log(email.to); // ["[email protected]"] Temporary Sandboxes
Temporary sandboxes auto-delete after a specified duration (1 to 60 seconds). They are ideal for test isolation because each test gets a clean, independent inbox that cleans itself up.
const sandbox = await client.sandboxes.createTemporary({
duration: 30,
name: 'signup-test'
});
// sandbox.isTemporary === true
// sandbox.deleteAfter === ISO timestamp For longer-running test suites, use persistent sandboxes and delete them manually:
const sandbox = await client.sandboxes.create({
name: 'e2e-tests'
});
// ... run tests ...
await client.sandboxes.delete(sandbox.id); Waiting for Emails
The waitFor method polls server-side and returns when a matching email arrives. No client-side polling loop needed.
// Wait for any email
const email = await client.emails.waitFor(sandbox.id);
// Wait for email from a specific sender
const email = await client.emails.waitFor(sandbox.id, {
from: '[email protected]'
});
// Wait for email with a subject substring match
const email = await client.emails.waitFor(sandbox.id, {
subject: 'Password Reset'
});
// Combine filters
const email = await client.emails.waitFor(sandbox.id, {
from: '[email protected]',
subject: 'Invoice',
timeout: 45000
}); The maximum timeout is 60 seconds (default is 30 seconds). If no matching email arrives, a TimeoutError is thrown.
Test Framework Examples
Vitest / Jest
import { describe, it, expect } from 'vitest';
import { MailloopClient } from '@mailloop/sdk';
const client = new MailloopClient({
apiKey: process.env.MAILLOOP_API_KEY
});
describe('signup emails', () => {
it('sends a welcome email after registration', async () => {
const sandbox = await client.sandboxes.createTemporary({
duration: 60
});
// Trigger your app to send email using sandbox SMTP credentials
await registerUser({
email: '[email protected]',
smtpHost: 'sandbox.mailloop.io',
smtpPort: 587,
smtpUser: sandbox.username,
smtpPass: sandbox.password
});
const email = await client.emails.waitFor(sandbox.id, {
subject: 'Welcome',
timeout: 15000
});
expect(email.subject).toContain('Welcome');
expect(email.to).toContain('[email protected]');
expect(email.html).toContain('Confirm your email');
});
}); Playwright (E2E)
import { test, expect } from '@playwright/test';
import { MailloopClient } from '@mailloop/sdk';
const client = new MailloopClient({
apiKey: process.env.MAILLOOP_API_KEY
});
test('password reset flow sends email with valid link', async ({ page }) => {
const sandbox = await client.sandboxes.createTemporary({
duration: 60
});
// Navigate to password reset page
await page.goto('/forgot-password');
await page.fill('[name="email"]', sandbox.emailAddress);
await page.click('button[type="submit"]');
// Wait for the reset email
const email = await client.emails.waitFor(sandbox.id, {
subject: 'Reset your password',
timeout: 30000
});
// Extract and follow the reset link
const resetLink = email.html.match(/href="([^"]*reset[^"]*)"/)?.[1];
expect(resetLink).toBeTruthy();
await page.goto(resetLink);
await expect(page.locator('h1')).toContainText('New Password');
}); CI/CD Integration
GitHub Actions
name: Email Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm test
env:
MAILLOOP_API_KEY: ${{ secrets.MAILLOOP_API_KEY }} Store MAILLOOP_API_KEY as a repository secret in GitHub. The SDK reads it from process.env.MAILLOOP_API_KEY in your test setup.
Environment Variables
For any CI provider, set these environment variables:
| Variable | Value | Description |
|---|---|---|
MAILLOOP_API_KEY | ml_live_... | Your API key |
The SDK only needs the API key. SMTP credentials are generated per-sandbox at runtime.
Tips
Parallel Tests
Each test can create its own temporary sandbox, so tests run in parallel without interfering with each other:
// test-a.spec.ts -- gets its own sandbox
const sandbox = await client.sandboxes.createTemporary({ duration: 60 });
// test-b.spec.ts -- gets a different sandbox
const sandbox = await client.sandboxes.createTemporary({ duration: 60 }); Listing Emails
If you need to check multiple emails instead of waiting for a specific one:
const { emails, total } = await client.emails.list(sandbox.id, {
limit: 10
});
for (const email of emails) {
console.log(`${email.from}: ${email.subject}`);
} Inspecting Full Email Details
The waitFor and get methods return the full email including HTML, text, headers, and attachment metadata:
const email = await client.emails.get(sandbox.id, emailId);
console.log(email.html); // Full HTML body
console.log(email.text); // Plain text body
console.log(email.headers); // All email headers
console.log(email.attachments); // Attachment metadata (filename, size, type) See the SDK Reference for the complete API.