Guide 9 of 9
Common SDK Patterns
Real-world code patterns for email testing. Copy, adapt, and use in your test suite. All examples support TypeScript, JavaScript, and curl.
OTP Extraction (Auto-Detect)
The SDK recognizes common OTP formats automatically. No regex needed for standard patterns.
const otp = await mf.emails.extractOtp(INBOX_ID, {
from: '[email protected]',
wait_timeout_ms: 15_000,
});
// → "482910"OTP Extraction (Custom Regex)
When auto-detect doesn't work, pass a custom regex pattern. The SDK returns the first capturing group.
const otp = await mf.emails.extractOtp(INBOX_ID, {
pattern: 'Activation PIN[:\s]+([A-Z0-9]{8})',
wait_timeout_ms: 15_000,
});
// → "X7K2P9QR"OTP with Alias Isolation
Isolate each test run with an ephemeral alias. Perfect for CI/CD where multiple tests run in parallel.
// Create ephemeral alias for this test run
const alias = await mf.inboxes.createAlias(INBOX_ID, {
tag: `run-${Date.now()}`,
ttl_hours: 1,
on_expire: 'delete',
});
// Send email to the alias address from your app:
// [email protected]
// Extract OTP scoped to this alias only
const otp = await mf.emails.extractOtp(INBOX_ID, {
alias_tag: alias.tag,
wait_timeout_ms: 20_000,
});Extract URL by Button Text
Find action links in email HTML by matching the visible button text. Most common pattern for magic links and password resets.
const url = await mf.emails.extractUrl(INBOX_ID, {
button_text: 'Verify Email',
wait_timeout_ms: 15_000,
});
// → "https://app.acme.com/verify?token=abc"Extract Multiple URLs (Approval Flows)
Use Promise.all to extract
different action links (Approve / Reject) from the same email.
// Extract two different buttons from the same email
const [approveUrl, rejectUrl] = await Promise.all([
mf.emails.extractUrl(INBOX_ID, {
subject: 'Action Required',
button_text: 'Approve',
wait_timeout_ms: 15_000,
}),
mf.emails.extractUrl(INBOX_ID, {
subject: 'Action Required',
button_text: 'Reject',
wait_timeout_ms: 15_000,
}),
]);Playwright: Magic Link Login
Use waitForUrl to
capture the link from an email, then navigate to it in the same test.
import { test, expect } from '@playwright/test';
import { withMailFork } from '@mailfork/sdk/playwright';
const mf = withMailFork({ apiKey: process.env.MF_API_KEY! });
const INBOX_ID = 'your-inbox-uuid';
test('magic link login', async ({ page }) => {
const magicUrl = await mf.waitForUrl(page, INBOX_ID, async () => {
await page.fill('[name=email]', '[email protected]');
await page.click('#send-magic-link');
}, {
button_text: 'Sign in',
from: '[email protected]',
});
await page.goto(magicUrl);
await expect(page).toHaveURL('/dashboard');
});waitForUrl records the current time,
triggers your async action, then polls for emails received after that timestamp.
This prevents picking up stale emails from previous tests.
Playwright: Isolated Alias per Test
Create an ephemeral alias before the test, send to that alias, and extract from it. Perfect for parallel CI runs.
test('verify email — isolated per test run', async ({ page }) => {
// Create an ephemeral alias; the address is ci+run-<ts>@qa.acme.mailfork.dev
const alias = await mfPlayer.sdk.inboxes.createAlias(INBOX_ID, {
tag: `run-${Date.now()}`,
ttl_hours: 1,
on_expire: 'delete',
});
const aliasAddress = `ci+${alias.tag}@qa.acme.mailfork.dev`;
const verifyUrl = await mfPlayer.waitForUrl(page, INBOX_ID, async () => {
await page.goto('/signup');
await page.fill('[name=email]', aliasAddress);
await page.click('#send-verification');
}, {
button_text: 'Verify Email',
alias_tag: alias.tag,
});
await page.goto(verifyUrl);
await expect(page).toHaveURL('/onboarding');
});Error Handling
The SDK exports typed errors. Catch and handle them explicitly.
import { NotFoundError, QuotaError, AuthError, ValidationError } from '@mailfork/sdk';
try {
const url = await mf.emails.extractUrl(INBOX_ID, {
button_text: 'Verify Email',
wait_timeout_ms: 15_000,
});
} catch (err) {
if (err instanceof NotFoundError) {
throw new Error('Verification email did not arrive within timeout');
}
if (err instanceof QuotaError) {
throw new Error('Daily API quota exceeded');
}
if (err instanceof AuthError) {
throw new Error('API key invalid or missing scope');
}
if (err instanceof ValidationError) {
throw new Error('Bad request parameters');
}
throw err;
}NotFoundError— Email did not arrive within timeoutQuotaError— API quota exceeded for your planAuthError— API key missing, invalid, or lacks required scope