API Documentation

MailBrew REST API Reference

Getting Started

MailBrew can be used in two ways.

SMTP
SMTP Mode (Email Send Testing)

Point your app's SMTP to MailBrew (smtp.mailbrew.dev). View sent emails in your dashboard's shared inbox in real time.

SMTP Config →
Cloud API
Cloud API Mode (E2E & CI Testing)

Create test email addresses via API, receive real emails, and automate OTP extraction, link verification, and email assertions. CI/CD ready.

Cloud API →

SMTP Configuration

Set the following environment variables in your application.

Use SMTP credentials generated in the Dashboard "SMTP Settings" page. All emails sent with these credentials are automatically routed to your company's shared inbox, regardless of the recipient address. No need to worry about recipient addresses during testing.

Laravel (.env)

Connections are encrypted. Use port 587 (STARTTLS, recommended) or 465 (SSL/TLS). Port 587 refuses authentication until the connection is encrypted.

MAIL_MAILER=smtp
MAIL_HOST=smtp.mailbrew.dev
MAIL_PORT=587
MAIL_USERNAME=mb_smtp_xxxxx
MAIL_PASSWORD=your_smtp_password
MAIL_ENCRYPTION=tls

Laravel 11+ encrypts automatically with MAIL_PORT=587 (MAIL_ENCRYPTION=tls is for Laravel 10 and earlier). For port 465 use MAIL_PORT=465 and MAIL_SCHEME=smtps (MAIL_ENCRYPTION=ssl on Laravel 10 and earlier).

For Node.js (Nodemailer):

const transporter = nodemailer.createTransport({
  host: 'smtp.mailbrew.dev',
  port: 587,
  secure: false,      // STARTTLS on 587 (for 465: port: 465, secure: true)
  requireTLS: true,
  auth: {
    user: 'mb_smtp_xxxxx',
    pass: 'your_smtp_password',
  },
});

Web UI

Emails sent to MailBrew can be viewed in real-time via the Web UI. Supports HTML preview, attachment downloads, and full-text search.

https://tokyo-rehearsal.mailbrew.dev

Cloud API

The following are MailBrew cloud service API endpoints. Authentication with an API key is required.

SDKs

Use our official SDKs for easy API integration.

npm install @mailbrew/sdk
const { MailBrew } = require('@mailbrew/sdk');
const client = new MailBrew('mb_your_key_here');

Methods

Address Management
createAddress(options?)

Create a temporary email address

const addr = await client.createAddress({ ttl: 3600 });
console.log(addr.address); // "ax7k2m@acme.mailbrew.dev"
listAddresses()

List all addresses

const addresses = await client.listAddresses();
deleteAddress(address)

Delete an address

await client.deleteAddress('ax7k2m@acme.mailbrew.dev');
Email Operations
waitForEmail(address, options?)

Wait for email via long-polling

const email = await client.waitForEmail(addr.address, { timeout: 30000 });
listEmails(address)

List emails for an address

const emails = await client.listEmails(addr.address);
getEmail(address, emailId)

Get email details

const detail = await client.getEmail(addr.address, email.id);
Data Extraction
extractOtp(address, emailId, options?)

Extract OTP/verification code from email

const code = await client.extractOtp(addr.address, email.id);
// Custom pattern: { pattern: '\\d{4}' }
extractLinks(address, emailId, options?)

Extract links from email

const links = await client.extractLinks(addr.address, email.id);
// Filter: { pattern: 'verify' }
assertEmail(address, emailId, assertions)

Assert email properties (subject, from, body, etc.)

const result = await client.assertEmail(addr.address, email.id, {
  subject_contains: 'Welcome',
  body_contains: 'verify',
});
console.log(result.passed); // true
Convenience
captureOtp(options?)

Create address + wait + extract OTP in one call

const { address, waitAndExtract } = await client.captureOtp({ ttl: 120 });
// ... trigger email ...
const { code, email } = await waitAndExtract();
forwardEmail(address, emailId, to)

Forward email to an active, verified member of your company

await client.forwardEmail(addr.address, email.id, 'dev@example.com');
Webhooks
createWebhook(url, options?)

Create a global webhook

const wh = await client.createWebhook('https://example.com/hook', {
  events: ['email.received'],
});
listWebhooks()

List webhooks

const webhooks = await client.listWebhooks();
deleteWebhook(id)

Delete a webhook

await client.deleteWebhook(wh.id);

Authentication

All API requests require an API key. Include it in the X-API-Key header.

X-API-Key: mb_your_key_here

Scopes

If an API key has scopes assigned, requests without the required scope will return a 403 error. Keys without scopes have full access.

addresses:read— List and view addresses
addresses:write— Create addresses
addresses:delete— Delete addresses
emails:read— List, wait for, extract OTP and links, assert, diff and check deliverability
emails:forward— Forward emails to active, verified members of your company
webhooks:read— List webhooks
webhooks:write— Create and update webhooks
webhooks:delete— Delete webhooks

Address Format

Generated addresses use your company subdomain. If a custom domain is configured, it will be used instead.

{random}@{company-slug}.mailbrew.dev

Base URL

https://tokyo-rehearsal.mailbrew.dev/api/mailbrew

API errors

Common causes for each status.

401
The API key is wrong or has been deleted. Check that you send it in the X-API-Key header.
403
The key does not have the scope for this operation, the request comes from an IP address outside your allowlist, the address or monthly limit is reached, or the account is disabled.
404
The address does not exist, belongs to another company or to another API key, the email was deleted after the retention period, the email ID is wrong, or no OTP was found. You can wait for an address that does not exist yet with the wait endpoint if it uses your own domain.
410
You called wait on an address created through the API whose TTL has expired. You can still read its emails during the company retention period (7 days by default).
422
A value is missing or has the wrong shape, local_part is already in use, or the assertion has no conditions. Send JSON with Content-Type: application/json.
429
You exceeded the calls per minute or the number of concurrent waits. Wait for the number of seconds in Retry-After.

The language of error messages follows the Accept-Language header (send Accept-Language: ja for Japanese). Without it, messages are in English. Identifiers such as scope names (emails:read) and codes are written the same way in every language.

Rate limit headers

The response headers tell you how many calls you have left.

Retry-After
How many seconds to wait (returned on 429).
X-RateLimit-Limit
The limit per minute.
X-RateLimit-Remaining
How many calls are left in this minute.

Verifying webhook signatures

Every delivery carries X-Mailbrew-Timestamp (the send time in UNIX seconds) and X-Mailbrew-Signature: sha256=…. Compare the received signature with the HMAC-SHA256 of "{timestamp}.{body}" computed with your signing secret, and compare them in constant time. Use the raw body bytes, and reject anything whose timestamp differs from the current time by more than 300 seconds (5 minutes).

X-Mailbrew-Timestamp
The send time in UNIX seconds.
X-Mailbrew-Signature
sha256=HMAC-SHA256(secret, "timestamp.body").
const timestamp = req.get('X-Mailbrew-Timestamp') || '';
if (!/^\d+$/.test(timestamp)) return res.sendStatus(401);
if (Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp)) > 300) return res.sendStatus(401);

const expected = 'sha256=' + crypto.createHmac('sha256', process.env.MAILBREW_WEBHOOK_SECRET)
  .update(timestamp + '.').update(req.body).digest('hex');

const received = Buffer.from(req.get('X-Mailbrew-Signature') || '');
const expectedBuffer = Buffer.from(expected);
if (received.length !== expectedBuffer.length || !crypto.timingSafeEqual(received, expectedBuffer)) {
  return res.sendStatus(401);
}

You can find the signing secret on the Webhooks page of the dashboard (owners only). Deliveries to a per-address webhook_url are signed with your company signing secret.

Endpoints

POST
/api/mailbrew/addresses
addresses:write

Create Address — Generate an email address. Set ttl=0 to create a permanent (non-expiring) address.

Parameters

ttlinteger
optional
Time to live in seconds. Default 3600. Maximum 31536000 (1 year). Set 0 for permanent
webhook_urlstring
optional
Notification URL on email receipt (per-address)
local_partstring
optional
Custom local part

Response

{
  "data": {
    "id": 1,
    "address": "ax7k2m@acme.mailbrew.dev",
    "webhook_url": null,
    "expires_at": "2026-03-29T13:00:00+00:00",
    "is_permanent": false,
    "created_at": "2026-03-29T12:00:00+00:00"
  }
}
GET
/api/mailbrew/addresses
addresses:read

List Addresses — Returns the addresses created with this API key and the addresses auto-created on receipt for the same company. Auto-created addresses can be read with any API key from the same company.

Response

{
  "data": [
    {
      "id": 1,
      "address": "ax7k2m@acme.mailbrew.dev",
      "webhook_url": null,
      "expires_at": "2026-03-29T13:00:00+00:00",
      "is_permanent": false,
      "created_at": "2026-03-29T12:00:00+00:00"
    }
  ]
}
GET
/api/mailbrew/addresses/{address}
addresses:read

Get Address — Get details of an address.

Response

{
  "data": {
    "id": 1,
    "address": "ax7k2m@acme.mailbrew.dev",
    "webhook_url": null,
    "expires_at": "2026-03-29T13:00:00+00:00",
    "is_permanent": false,
    "created_at": "2026-03-29T12:00:00+00:00"
  }
}
GET
/api/mailbrew/addresses/{address}/emails
emails:read

List Emails — List emails received at the specified address. Emails are deleted after the retention period (7 days on every plan; organizations can shorten it in Security settings).

Response

{
  "data": [
    {
      "id": 1,
      "from_address": "noreply@example.com",
      "subject": "Your verification code",
      "text_body": "Your code is: 847291",
      "received_at": "2026-03-29T12:05:00+00:00"
    }
  ]
}
GET
/api/mailbrew/addresses/{address}/emails/{id}
emails:read

Get Email — Get details of a specific email.

Response

{
  "data": {
    "id": 1,
    "from_address": "noreply@example.com",
    "subject": "Your verification code",
    "text_body": "Your code is: 847291",
    "html_body": "<p>Your code is: <b>847291</b></p>",
    "received_at": "2026-03-29T12:05:00+00:00"
  }
}
GET
/api/mailbrew/addresses/{address}/emails/{id}/otp
emails:read

Extract OTP — Auto-extract OTP/verification codes from email body. Perfect for E2E tests.

Parameters

patternstring
optional
Custom regex pattern. Write a capture group ( ) to get the captured part instead of the whole match (e.g. Code: (\d{6}))

Response

{
  "data": {
    "code": "847291",
    "email_id": 1,
    "source": "text_body"
  }
}
GET
/api/mailbrew/addresses/{address}/emails/{id}/links
emails:read

Extract Links — Auto-extract links (URLs) from email body. Perfect for invitation links and password reset flows.

Parameters

patternstring
optional
Regex pattern to filter links by domain

Response

{
  "data": {
    "links": [
      "https://example.com/verify?token=abc123",
      "https://example.com/unsubscribe"
    ],
    "email_id": 1,
    "source": "text_body"
  }
}
POST
/api/mailbrew/addresses/{address}/emails/{id}/assert
emails:read

Assert Email — Validate email content. Check subject, sender, recipient, and body content simultaneously.

Parameters

subjectstring
optional
Exact subject match
subject_containsstring
optional
Subject contains
fromstring
optional
Exact from address match
tostring
optional
To addresses contains
body_containsstring
optional
Body contains

Response

{
  "data": {
    "passed": true,
    "email_id": 1,
    "results": [
      { "field": "subject", "passed": true, "expected": "Welcome", "actual": "Welcome" },
      { "field": "body_contains", "passed": true, "expected": "verify" }
    ]
  }
}
POST
/api/mailbrew/addresses/{address}/emails/{id}/forward
emails:forward

Forward Email — Forward a received email only to an active, verified member of your company.

Parameters

tostring
required
Email address of an active, verified member of your company

Response

{
  "data": {
    "forwarded": true,
    "to": "dev@example.com"
  },
  "success": true
}
DELETE
/api/mailbrew/addresses/{address}
addresses:delete

Delete Address — Delete an address.

Response

204 No Content
GET
/api/mailbrew/addresses/{address}/emails/wait
emails:read

Wait for Emails — Wait for emails using long-polling.

Parameters

timeoutinteger
optional
Wait seconds (1-60, default 30)
sincestring
optional
Emails received at or after this ISO8601 timestamp (rounded to the second)
countinteger
optional
Number of emails to wait for (1-100, default 1)

Response

{ "data": [] }
POST
/api/mailbrew/addresses/{address}/sequence
emails:read

Verify Email Sequence — Verify that matching emails arrive in order.

Parameters

stepsarray
required
Array of assertion conditions for each email
timeoutinteger
optional
Wait seconds (1-60, default 30)

Response

{ "data": { "passed": true, "steps": [] } }
POST
/api/mailbrew/addresses/{address}/emails/{id}/diff
emails:read

Compare Emails — Compare two emails.

Parameters

againstinteger
required
Email ID to compare against

Response

{ "data": { "email_id": 1, "against_id": 2, "fields": {} } }
GET
/api/mailbrew/addresses/{address}/emails/{id}/deliverability
emails:read

Analyze Deliverability — Analyze deliverability including SPF, DKIM, and DMARC.

Response

{ "data": { "score": 100, "checks": [], "recommendations": [] } }
GET
/api/mailbrew/emails
emails:read

List Captured Emails — List emails sent over SMTP AUTH to external recipients (e.g. gmail.com), looked up by envelope recipient (RCPT TO), including BCC. A MailBrew address cannot be used as to (use the address endpoints). Get one, extract OTP or links with /api/mailbrew/emails/{id}, /otp and /links.

Parameters

tostring
required
Envelope recipient email address
sincestring
optional
Emails received at or after this ISO8601 timestamp (rounded to the second)

Response

{
  "data": [
    {
      "id": 1,
      "from_address": "noreply@example.com",
      "subject": "Your verification code",
      "text_body": "Your code is: 847291",
      "received_at": "2026-03-29T12:05:00+00:00"
    }
  ]
}
GET
/api/mailbrew/emails/wait
emails:read

Wait for Captured Emails — Wait for captured emails using long-polling. Same parameters as waiting on an address.

Parameters

tostring
required
Envelope recipient email address
timeoutinteger
optional
Wait seconds (1-60, default 30)
sincestring
optional
Emails received at or after this ISO8601 timestamp (rounded to the second)
countinteger
optional
Number of emails to wait for (1-100, default 1)

Response

{ "data": [] }
GET
/api/mailbrew/webhooks
webhooks:read

List Webhooks — List global webhooks.

Response

{
  "data": [
    {
      "id": 1,
      "url": "https://example.com/hook",
      "events": ["email.received"],
      "is_active": true
    }
  ]
}
POST
/api/mailbrew/webhooks
webhooks:write

Create Webhook — Register a global webhook. Notifies for all addresses.

Parameters

urlstring
required
Webhook URL
eventsarray
optional
Event array. Empty for all events

Response

{
  "data": {
    "id": 1,
    "url": "https://example.com/hook",
    "secret": "abc123...",
    "events": ["email.received"],
    "is_active": true
  }
}
PUT
/api/mailbrew/webhooks/{id}
webhooks:write

Update Webhook — Update a webhook URL, events, or active state.

Parameters

urlstring
optional
Webhook URL
eventsarray
optional
Event array
is_activeboolean
optional
Active state

Response

{ "data": { "id": 1, "url": "https://example.com/hook", "events": ["email.received"], "is_active": true } }
DELETE
/api/mailbrew/webhooks/{id}
webhooks:delete

Delete Webhook — Delete a webhook.

Response

204 No Content

Code Examples

const { MailBrew } = require('@mailbrew/sdk');
const client = new MailBrew('mb_your_key_here');

// E2E test: create address, wait for email, extract OTP
const { address, waitAndExtract } = await client.captureOtp({ ttl: 120 });

// ... trigger email send to `address` ...

const { code, email } = await waitAndExtract();
console.log(code); // "847291"