Getting Started with Ephemail

Learn how to integrate Ephemail into your automated testing workflows with Playwright, Cypress, or any testing framework.

Prerequisites

API Overview

Ephemail provides a simple REST API for creating temporary email inboxes and retrieving messages. All examples use the native Fetch API - no additional packages required.

Configuration

First, create an API key in your dashboard, then add your credentials to your environment variables:

# .env
EPHEMAIL_API_URL=https://api.ephemail.declytic.com/v1
EPHEMAIL_API_KEY=eph_your_api_key_here

Basic Usage

Create a simple example to verify your setup:

const API_URL = process.env.EPHEMAIL_API_URL!;
const API_KEY = process.env.EPHEMAIL_API_KEY!;

// Helper function for API calls
async function ephemailApi(endpoint: string, options: RequestInit = {}) {
  const response = await fetch(`${API_URL}${endpoint}`, {
    ...options,
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json',
      ...options.headers,
    },
  });
  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }
  return response.json();
}

// Create a temporary inbox
const inbox = await ephemailApi('/inboxes', {
  method: 'POST',
  body: JSON.stringify({
    expiresInSeconds: 1800, // 30 minutes
    tags: { test: 'example' },
  }),
});

console.log(`Created inbox: ${inbox.emailAddress}`);

// Poll for latest email (with retry logic)
let message = null;
for (let i = 0; i < 10; i++) {
  const messages = await ephemailApi(`/inboxes/${inbox.id}/messages?limit=1`);
  if (messages.length > 0) {
    message = messages[0];
    break;
  }
  await new Promise(resolve => setTimeout(resolve, 2500));
}

if (message) {
  console.log(`Received email: ${message.subject}`);
}

// Clean up
await ephemailApi(`/inboxes/${inbox.id}`, { method: 'DELETE' });

Playwright Integration

Integrate Ephemail with Playwright to test magic-link authentication flows:

import { test, expect } from '@playwright/test';

const API_URL = process.env.EPHEMAIL_API_URL!;
const API_KEY = process.env.EPHEMAIL_API_KEY!;

// Helper function for Ephemail API
async function ephemailApi(endpoint: string, options: RequestInit = {}) {
  const response = await fetch(`${API_URL}${endpoint}`, {
    ...options,
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json',
      ...options.headers,
    },
  });
  if (!response.ok) throw new Error(`API error: ${response.status}`);
  return response.json();
}

// Extract magic link from email HTML
function extractMagicLink(html: string): string | null {
  const linkMatch = html.match(/href=["'](https?:\/\/[^"']*(?:magic|verify|auth|login)[^"']*)["']/i);
  return linkMatch ? linkMatch[1] : null;
}

test('Complete magic link signup flow', async ({ page }) => {
  // Create temporary inbox
  const inbox = await ephemailApi('/inboxes', {
    method: 'POST',
    body: JSON.stringify({ expiresInSeconds: 1800 }),
  });
  console.log(`Using email: ${inbox.emailAddress}`);

  // Navigate to signup page
  await page.goto('https://app.example.com/signup');

  // Fill in the email form
  await page.fill('input[name="email"]', inbox.emailAddress);
  await page.click('button[type="submit"]');

  // Wait for confirmation message
  await expect(page.locator('text=Check your email')).toBeVisible();

  // Poll for the magic link email
  let message = null;
  for (let i = 0; i < 10; i++) {
    const messages = await ephemailApi(`/inboxes/${inbox.id}/messages?limit=1`);
    if (messages.length > 0 && messages[0].subject?.toLowerCase().includes('magic')) {
      message = messages[0];
      break;
    }
    await page.waitForTimeout(2500);
  }

  expect(message).toBeDefined();

  // Extract and visit the magic link
  const magicLink = extractMagicLink(message!.htmlBody || message!.textBody);
  expect(magicLink).toBeDefined();

  await page.goto(magicLink!);

  // Verify successful authentication
  await expect(page).toHaveURL(/\/dashboard/);
  await expect(page.locator('text=Welcome')).toBeVisible();

  // Clean up
  await ephemailApi(`/inboxes/${inbox.id}`, { method: 'DELETE' });
});

Cypress Integration

Use Ephemail in Cypress tests:

// Helper function for Ephemail API
async function ephemailApi(endpoint: string, options: RequestInit = {}) {
  const API_URL = Cypress.env('EPHEMAIL_API_URL');
  const API_KEY = Cypress.env('EPHEMAIL_API_KEY');
  
  const response = await fetch(`${API_URL}${endpoint}`, {
    ...options,
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json',
      ...options.headers,
    },
  });
  if (!response.ok) throw new Error(`API error: ${response.status}`);
  return response.json();
}

function extractMagicLink(html: string): string | null {
  const linkMatch = html.match(/href=["'](https?:\/\/[^"']*(?:magic|verify|auth|login)[^"']*)["']/i);
  return linkMatch ? linkMatch[1] : null;
}

describe('Magic Link Authentication', () => {
  let inbox: any;

  it('should complete magic link flow', async () => {
    // Create inbox
    inbox = await ephemailApi('/inboxes', {
      method: 'POST',
      body: JSON.stringify({ expiresInSeconds: 1800 }),
    });

    // Visit signup page
    cy.visit('/signup');
    cy.get('input[name="email"]').type(inbox.emailAddress);
    cy.get('button[type="submit"]').click();

    // Poll for email
    let message = null;
    for (let i = 0; i < 10; i++) {
      const messages = await ephemailApi(`/inboxes/${inbox.id}/messages?limit=1`);
      if (messages.length > 0) {
        message = messages[0];
        break;
      }
      cy.wait(2500);
    }

    // Extract magic link
    const magicLink = extractMagicLink(message!.htmlBody || message!.textBody);

    // Visit magic link
    cy.visit(magicLink!);

    // Verify authenticated
    cy.url().should('include', '/dashboard');
    cy.contains('Welcome').should('be.visible');
  });

  after(async () => {
    if (inbox) {
      await ephemailApi(`/inboxes/${inbox.id}`, { method: 'DELETE' });
    }
  });
});

Advanced Usage

Filtering Emails

Use query parameters to filter for specific emails:

// Poll for a specific email with filters
const sinceTimestamp = Date.now() - 60000; // Last minute
let targetMessage = null;

for (let i = 0; i < 10; i++) {
  const messages = await ephemailApi(
    `/inboxes/${inbox.id}/messages?limit=10&since=${sinceTimestamp}`
  );
  
  // Filter in application code
  targetMessage = messages.find((msg: any) => 
    msg.from?.includes('noreply@example.com') &&
    msg.subject?.includes('Verify your email')
  );
  
  if (targetMessage) break;
  await new Promise(resolve => setTimeout(resolve, 2500));
}

Extracting Links

Extract all links from an email:

// Extract all URLs from email HTML or text
function extractAllLinks(html: string): string[] {
  const linkRegex = /href=["'](https?:\/\/[^"']+)["']/gi;
  const matches = [...html.matchAll(linkRegex)];
  return matches.map(match => match[1]);
}

const links = extractAllLinks(message.htmlBody || message.textBody);
for (const link of links) {
  console.log(link);
}

// Or find magic/auth links specifically
function findMagicLink(html: string, allowedHosts: string[] = []): string | null {
  const links = extractAllLinks(html);
  return links.find(url => {
    const hasAuthPattern = /(?:magic|verify|auth|login|signin|signup)/i.test(url);
    const matchesHost = allowedHosts.length === 0 || 
      allowedHosts.some(host => url.includes(host));
    return hasAuthPattern && matchesHost;
  }) || null;
}

const magicLink = findMagicLink(message.htmlBody, ['auth.example.com']);

Managing Multiple Inboxes

Create and manage multiple inboxes for parallel tests:

const inboxes = await Promise.all([
  ephemailApi('/inboxes', {
    method: 'POST',
    body: JSON.stringify({ tags: { test: 'user-1' } }),
  }),
  ephemailApi('/inboxes', {
    method: 'POST',
    body: JSON.stringify({ tags: { test: 'user-2' } }),
  }),
  ephemailApi('/inboxes', {
    method: 'POST',
    body: JSON.stringify({ tags: { test: 'user-3' } }),
  }),
]);

// Run tests in parallel
await Promise.all(
  inboxes.map(inbox => runTestWithInbox(inbox))
);

// Clean up all inboxes
await Promise.all(
  inboxes.map(inbox => ephemailApi(`/inboxes/${inbox.id}`, { method: 'DELETE' }))
);

Best Practices

  • Always clean up: Delete inboxes after tests to avoid hitting limits
  • Use appropriate timeouts: Set realistic timeouts based on your email provider
  • Tag your inboxes: Use tags to organize and track test inboxes
  • Handle failures gracefully: Implement retries for email waiting
  • Use filters: Filter emails to reduce false matches and improve reliability

Troubleshooting

Email Not Received

  • Verify the email was sent to the correct address
  • Check that the inbox hasn't expired
  • Increase timeout and retry values
  • Check spam/filter settings on the sender side

API Errors

  • Verify your API key is correct
  • Check that you haven't exceeded your plan limits
  • Ensure the API URL is correct for your environment

Next Steps