Quick Start
Get started testing emails with plop.email in 5 minutes.
Add email testing to your application in minutes. No mail servers to configure—just API calls.
1. Get Your API Key
Sign up at plop.email and create an API key from your dashboard.
2. Choose Your Integration
import { test, expect } from '@playwright/test';
test('signup sends welcome email', async ({ page, request }) => {
const testEmail = `signup+${Date.now()}@in.plop.email`;
// Trigger signup
await page.goto('/signup');
await page.fill('[name="email"]', testEmail);
await page.fill('[name="password"]', 'SecurePass123!');
await page.click('button[type="submit"]');
// Wait and fetch email
await page.waitForTimeout(2000);
const response = await request.get(
'https://api.plop.email/v1/messages/latest',
{
params: { to: testEmail },
headers: { Authorization: `Bearer ${process.env.PLOP_API_KEY}` },
}
);
const email = await response.json();
// Assert
expect(email.data.subject).toContain('Welcome');
expect(email.data.htmlContent).toContain('Get Started');
});// cypress/support/commands.ts
Cypress.Commands.add('getLatestEmail', (to: string) => {
return cy.request({
method: 'GET',
url: 'https://api.plop.email/v1/messages/latest',
headers: { Authorization: `Bearer ${Cypress.env('PLOP_API_KEY')}` },
qs: { to },
}).its('body.data');
});
// cypress/e2e/signup.cy.ts
it('sends welcome email', () => {
const testEmail = `signup+${Date.now()}@in.plop.email`;
cy.visit('/signup');
cy.get('[name="email"]').type(testEmail);
cy.get('[name="password"]').type('SecurePass123!');
cy.get('form').submit();
cy.wait(2000);
cy.getLatestEmail(testEmail).then((email) => {
expect(email.subject).to.include('Welcome');
});
});import { sendWelcomeEmail } from '../src/email';
async function fetchEmail(to: string) {
const response = await fetch(
`https://api.plop.email/v1/messages/latest?to=${encodeURIComponent(to)}`,
{ headers: { Authorization: `Bearer ${process.env.PLOP_API_KEY}` } }
);
return (await response.json()).data;
}
test('sends welcome email', async () => {
const testEmail = `welcome+${Date.now()}@in.plop.email`;
await sendWelcomeEmail({ to: testEmail, name: 'Test User' });
await new Promise(r => setTimeout(r, 2000));
const email = await fetchEmail(testEmail);
expect(email.subject).toBe('Welcome to Our App!');
expect(email.htmlContent).toContain('Test User');
});import pytest
import requests
import time
import os
@pytest.fixture
def plop_client():
class PlopClient:
def get_latest(self, to: str):
response = requests.get(
"https://api.plop.email/v1/messages/latest",
params={"to": to},
headers={"Authorization": f"Bearer {os.environ['PLOP_API_KEY']}"},
)
return response.json()["data"]
return PlopClient()
def test_welcome_email(plop_client):
test_email = f"pytest+{int(time.time())}@in.plop.email"
# Send email from your app
send_welcome_email(to=test_email, name="Test User")
time.sleep(2)
# Verify
email = plop_client.get_latest(test_email)
assert "Welcome" in email["subject"]
assert "Test User" in email["htmlContent"]# Fetch latest email for a specific address
curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://api.plop.email/v1/messages/latest?to=test@in.plop.email"
# Response
{
"data": {
"id": "msg_abc123",
"subject": "Welcome!",
"from": "hello@yourapp.com",
"to": "test@in.plop.email",
"htmlContent": "<html>...</html>",
"textContent": "Welcome to our app!",
"receivedAt": "2025-01-01T12:00:00.000Z"
}
}Using an SDK? The official TypeScript SDK and Python SDK handle polling and error handling for you — no manual fetch or waitForTimeout needed.
3. Key Concepts
Unique Test Addresses
Generate a unique email for each test to avoid conflicts:
const testEmail = `signup+${Date.now()}@in.plop.email`;
// Result: signup+1704067200000@in.plop.emailThe +tag part (plus-addressing) isolates each test run while using the same mailbox.
Polling for Email Arrival
Emails may take 1-3 seconds to arrive. Use polling with retry logic:
async function waitForEmail(to: string, timeout = 10000) {
const start = Date.now();
while (Date.now() - start < timeout) {
const response = await fetch(
`https://api.plop.email/v1/messages/latest?to=${to}`,
{ headers: { Authorization: `Bearer ${process.env.PLOP_API_KEY}` } }
);
if (response.ok) {
const { data } = await response.json();
if (data) return data;
}
await new Promise(r => setTimeout(r, 1000));
}
throw new Error(`No email received for ${to}`);
}Tip: The official SDKs handle polling automatically with plop.messages.waitFor() (TypeScript) or plop.messages.wait_for() (Python). See TypeScript SDK or Python SDK.
Extract Links and Tokens
Common patterns for extracting verification links and OTPs:
// Extract link from HTML
const linkMatch = email.htmlContent.match(/href="([^"]*verify[^"]*)"/);
const verifyLink = linkMatch?.[1];
// Extract 6-digit OTP from text
const otpMatch = email.textContent.match(/\b(\d{6})\b/);
const otp = otpMatch?.[1];Environment Setup
CI/CD (GitHub Actions)
- name: Run E2E tests
run: npm run test:e2e
env:
PLOP_API_KEY: ${{ secrets.PLOP_API_KEY }}Local Development
# .env
PLOP_API_KEY=your_api_key_hereResponse Fields
The API returns these fields for each message:
| Field | Type | Description |
|---|---|---|
id | string | Unique message ID |
subject | string | Email subject line |
from | string | Sender address |
to | string | Recipient address |
htmlContent | string | HTML body content |
textContent | string | Plain text content |
receivedAt | string | ISO timestamp |
headers | object | Email headers |
Next Steps
- Messages API Reference — Full endpoint documentation
- E2E Email Polling — Advanced polling patterns
- Inbox Domains & Mailboxes — Custom domains and tags