| name | resend |
| description | Emulated Resend email API for local development and testing. Use when the user needs to send emails locally, test transactional email flows, implement magic link or verification code auth, inspect sent emails, manage domains/contacts/API keys, or work with the Resend API without sending real emails. Triggers include "Resend API", "emulate Resend", "send email locally", "test email", "magic link", "verification email", "email inbox", "RESEND_BASE_URL", or any task requiring a local email API. |
| allowed-tools | Bash(npx emulate:*), Bash(emulate:*), Bash(curl:*) |
Resend Email API Emulator
Fully stateful Resend API emulation. Emails, domains, API keys, audiences, and contacts persist in memory. Sent emails are captured and viewable through the inbox UI or the REST API.
No real emails are sent. Every call to POST /emails stores the message locally so you can inspect it programmatically or in the browser.
Start
npx emulate --service resend
Or programmatically:
import { createEmulator } from 'emulate'
const resend = await createEmulator({ service: 'resend', port: 4000 })
Auth
Pass tokens as Authorization: Bearer <token>. Any re_ prefixed token is accepted.
curl http://localhost:4000/emails \
-H "Authorization: Bearer re_test_key"
When no token is provided, requests fall back to the default user.
Pointing Your App at the Emulator
Environment Variable (Resend SDK)
The official Resend Node.js SDK reads RESEND_BASE_URL at module load time. Set it to the emulator URL and the SDK works without any code changes:
RESEND_BASE_URL=http://localhost:4000
import { Resend } from 'resend'
const resend = new Resend('re_test_key')
await resend.emails.send({
from: 'hello@example.com',
to: 'user@example.com',
subject: 'Hello',
html: '<p>It works!</p>',
})
Embedded in Next.js (adapter-next)
When using @emulators/adapter-next, the emulator runs inside your Next.js app at /emulate/resend. Set RESEND_BASE_URL via next.config.ts:
import { withEmulate } from '@emulators/adapter-next'
export default withEmulate({
env: {
RESEND_BASE_URL: `http://localhost:${process.env.PORT ?? '3000'}/emulate/resend`,
},
})
import { createEmulateHandler } from '@emulators/adapter-next'
import * as resend from '@emulators/resend'
export const { GET, POST, PUT, PATCH, DELETE } = createEmulateHandler({
services: {
resend: {
emulator: resend,
seed: {
domains: [{ name: 'example.com' }],
},
},
},
})
Direct fetch
If you cannot use the SDK or env var, call the emulator directly:
await fetch('http://localhost:4000/emails', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer re_test_key',
},
body: JSON.stringify({
from: 'hello@example.com',
to: 'user@example.com',
subject: 'Hello',
html: '<p>It works!</p>',
}),
})
Seed Config
resend:
domains:
- name: example.com
region: us-east-1
contacts:
- email: test@example.com
first_name: Test
last_name: User
audience: Default
Retrieving Sent Emails
This is the key differentiator of the emulator: every email sent via POST /emails is stored and queryable.
Inbox UI
Browse sent emails in the browser:
http://localhost:4000/inbox
REST API
curl http://localhost:4000/emails \
-H "Authorization: Bearer re_test_key"
curl http://localhost:4000/emails/<id> \
-H "Authorization: Bearer re_test_key"
Extracting Data from Emails (tests, agents)
Useful for completing magic link, verification code, or password reset flows programmatically:
EMAIL_ID=$(curl -s http://localhost:4000/emails \
-H "Authorization: Bearer re_test_key" | jq -r '.data[0].id')
CODE=$(curl -s http://localhost:4000/emails/$EMAIL_ID \
-H "Authorization: Bearer re_test_key" | jq -r '.html' | grep -oE '[0-9]{6}')
echo "Verification code: $CODE"
API Endpoints
Emails
curl -X POST http://localhost:4000/emails \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"from": "hello@example.com", "to": "user@example.com", "subject": "Hello", "html": "<p>Hi</p>"}'
curl -X POST http://localhost:4000/emails/batch \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '[{"from": "a@example.com", "to": "b@example.com", "subject": "One", "html": "<p>1</p>"}]'
curl http://localhost:4000/emails \
-H "Authorization: Bearer $TOKEN"
curl http://localhost:4000/emails/<id> \
-H "Authorization: Bearer $TOKEN"
curl -X POST http://localhost:4000/emails/<id>/cancel \
-H "Authorization: Bearer $TOKEN"
Supported fields: from, to, subject, html, text, cc, bcc, reply_to, headers, tags, scheduled_at.
Domains
curl -X POST http://localhost:4000/domains \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "example.com", "region": "us-east-1"}'
curl http://localhost:4000/domains \
-H "Authorization: Bearer $TOKEN"
curl http://localhost:4000/domains/<id> \
-H "Authorization: Bearer $TOKEN"
curl -X POST http://localhost:4000/domains/<id>/verify \
-H "Authorization: Bearer $TOKEN"
curl -X DELETE http://localhost:4000/domains/<id> \
-H "Authorization: Bearer $TOKEN"
API Keys
curl -X POST http://localhost:4000/api-keys \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Production"}'
curl http://localhost:4000/api-keys \
-H "Authorization: Bearer $TOKEN"
curl -X DELETE http://localhost:4000/api-keys/<id> \
-H "Authorization: Bearer $TOKEN"
Audiences
curl -X POST http://localhost:4000/audiences \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Newsletter"}'
curl http://localhost:4000/audiences \
-H "Authorization: Bearer $TOKEN"
curl -X DELETE http://localhost:4000/audiences/<id> \
-H "Authorization: Bearer $TOKEN"
Contacts
curl -X POST http://localhost:4000/audiences/<audience_id>/contacts \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "first_name": "Jane", "last_name": "Doe"}'
curl http://localhost:4000/audiences/<audience_id>/contacts \
-H "Authorization: Bearer $TOKEN"
curl -X DELETE http://localhost:4000/audiences/<audience_id>/contacts/<id> \
-H "Authorization: Bearer $TOKEN"
Webhooks
The emulator dispatches webhook events when state changes:
email.sent and email.delivered on POST /emails
domain.created and domain.deleted on domain operations
contact.created and contact.deleted on contact operations
Common Patterns
Magic Link / Verification Code Flow
TOKEN="re_test_key"
BASE="http://localhost:4000"
curl -X POST $BASE/emails \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"from": "auth@example.com", "to": "user@example.com", "subject": "Your code", "html": "<p>Code: <strong>482910</strong></p>"}'
EMAIL_ID=$(curl -s $BASE/emails -H "Authorization: Bearer $TOKEN" | jq -r '.data[0].id')
curl -s $BASE/emails/$EMAIL_ID -H "Authorization: Bearer $TOKEN" | jq -r '.html'
Send and Verify in a Test
import { createEmulator } from 'emulate'
import { Resend } from 'resend'
const emu = await createEmulator({ service: 'resend', port: 4000 })
process.env.RESEND_BASE_URL = emu.url
const resend = new Resend('re_test_key')
await resend.emails.send({
from: 'auth@example.com',
to: 'user@test.com',
subject: 'Verify',
html: '<p>Code: <strong>123456</strong></p>',
})
const res = await fetch(`${emu.url}/emails`, {
headers: { Authorization: 'Bearer re_test_key' },
})
const { data: emails } = await res.json()
console.log(emails[0].html)