Documentation

Everything you need to integrate MX Forwarder into your application.

For AI Agents & Coding Assistants

Building an integration with an AI coding assistant? These machine-readable formats are optimized for LLMs:

Tip: paste the content of /docs/api.md into your AI assistant, or use the MCP server for direct tool integration.

Quick Start

1

Create an account

Sign up at mx-forwarder.com and generate an API key from the dashboard.

2

Create an address

POST /api/addresses to get a unique address like invoices@mx-forwarder.com.

3

Receive webhooks

Emails sent to your address are parsed and POSTed to your webhook URL as signed JSON.

Authentication

All API requests require a Bearer token. Generate one from Dashboard > API Keys.

bash
curl -H "Authorization: Bearer mf_live_your_api_key" \
  https://mx-forwarder.com/api/addresses

API Reference

Base URL: https://mx-forwarder.com

Addresses

GET/api/addresses— List all forwarding addresses
POST/api/addresses— Create a new forwarding address

Request Body (JSON)

local_partstring— Local part of the email (e.g. "invoices"). Random if omitted.
webhook_urlstring— URL to forward emails to
webhook_format"json" | "raw"— Payload format (default: json)
webhook_method"POST" | "GET" | "PUT" | "PATCH"— HTTP method (default: POST)
attachment_mode"url" | "inline"— "url" for signed R2 links, "inline" for base64
simulationboolean— Enable test mode (no actual forwarding)
store_attachmentsboolean— Store attachments in R2

Response

{
  "address": { "id": "01J...", "local_part": "invoices", "email": "invoices@mx-forwarder.com" },
  "webhook_secret": "a1b2c3d4-..."
}
GET/api/addresses/{id}— Get a single address
PUT/api/addresses/{id}— Update address configuration

Request Body (JSON)

webhook_urlstring— New webhook URL
webhook_format"json" | "raw"— Payload format
webhook_method"POST" | "GET" | "PUT" | "PATCH"— HTTP method
attachment_mode"url" | "inline"— Attachment delivery mode
simulationboolean— Test mode
store_attachmentsboolean— R2 storage
is_activeboolean— Enable/disable the address
DELETE/api/addresses/{id}— Delete an address and all associated data

Emails

GET/api/emails— List received emails with pagination and filtering

Query Parameters

qstring— Full-text search query
address_idstring— Filter by address ID
limitnumber— Results per page (default 50, max 100)
cursorstring— Pagination cursor from previous response

Response

{
  "emails": [{ "id": "01J...", "subject": "Invoice #1234", "from_addr": "sender@example.com", ... }],
  "next_cursor": "2026-04-29T09:00:00Z"
}
GET/api/emails/{id}— Get full email with attachments and delivery history

Response

{
  "email": { "id": "01J...", "subject": "...", "text_body": "...", "html_body": "..." },
  "attachments": [{ "id": "01J...", "file_name": "invoice.pdf", "content_type": "application/pdf", "size_bytes": 54321 }],
  "deliveries": [{ "id": "01J...", "status": "delivered", "http_status": 200, "attempt_count": 1 }]
}
GET/api/emails/{id}/raw— Download original RFC822 .eml file (binary, not JSON)
POST/api/emails/{id}/retry— Re-queue email for webhook delivery

Deliveries

GET/api/deliveries— List webhook delivery logs

Query Parameters

address_idstring— Filter by address ID
email_idstring— Filter by email ID
status"pending" | "delivered" | "failed"— Filter by status
limitnumber— Results per page (default 50, max 100)
cursorstring— Pagination cursor

Response

{
  "deliveries": [{ "id": "01J...", "status": "delivered", "http_status": 200, "attempt_count": 1 }],
  "next_cursor": "2026-04-29T09:00:00Z"
}

Usage

GET/api/usage— Current billing period usage and plan limits

Response

{
  "plan": "starter", "month": "2026-04",
  "email_count": 142, "email_limit": 25000,
  "attachment_bytes": 10485760, "attachment_limit": 2147483648,
  "addresses_used": 3, "addresses_limit": 25
}

Reseller (requires reseller plan)

GET/api/reseller/sub-accounts— List all sub-accounts

Response

{
  "sub_accounts": [{ "id": "01J...", "email": "feeder-abc123@newsletter.acme.com", "plan": "free", "external_id": "usr_abc123", "created_at": "..." }],
  "total": 1
}
POST/api/reseller/sub-accounts— Create sub-account. Email is an identifier (can be synthetic). Returns API key (once).

Request Body (JSON)

emailstringrequired— Identifier (can be synthetic, e.g. feeder-{userId}@your-domain.com)
planstring— Plan for address/retention limits (default: "free")
external_idstring— Your own customer ID for mapping

Response

{
  "sub_account": { "id": "01J...", "email": "feeder-abc123@newsletter.acme.com", "plan": "free", "external_id": "usr_abc123", "created_at": "..." },
  "api_key": "mf_live_abc123..."
}
PATCH/api/reseller/sub-accounts/{id}— Update sub-account plan or external_id

Request Body (JSON)

planstring— New plan for the sub-account
external_idstring— Update your customer reference
DELETE/api/reseller/sub-accounts/{id}— Delete sub-account and all its data
GET/api/reseller/usage— Aggregate usage across all sub-accounts

Query Parameters

monthstring— Billing month in YYYY-MM format (default: current)

Response

{
  "month": "2026-04",
  "aggregate": { "email_count": 15420, "attachment_bytes": 52428800 },
  "limits": { "emails_per_month": 1000000, "attachment_storage_bytes": 107374182400 },
  "sub_accounts": [{ "user_id": "01J...", "email": "alice@acme.com", "email_count": 8200, "attachment_bytes": 30000000 }]
}

Webhook Payload

Every email is POSTed to your webhook URL with an HMAC-SHA256 signature in the X-MailForwarder-Signature header.

json
{
  "id": "01JQWX...",
  "envelope": {
    "from": "sender@example.com",
    "to": "invoices@mx-forwarder.com"
  },
  "headers": { "subject": "Invoice #1234", "from": "...", "to": "...", "date": "..." },
  "plain": "Please find attached...",
  "html": "...",
  "attachments": [
    {
      "file_name": "invoice.pdf",
      "content_type": "application/pdf",
      "size": 12345,
      "url": "https://signed-r2-url..."
    }
  ],
  "spf": { "result": "pass" },
  "dkim": { "result": "pass" },
  "dmarc": { "result": "pass" }
}

Pagination

List endpoints use cursor-based pagination. The response includes a next_cursor field. Pass it as the cursor query parameter to get the next page. When next_cursor is null, there are no more results. Max limit is 100.

Error Handling

All errors return a consistent JSON structure:

json
{ "error": "Descriptive error message", "status": 401 }
400Invalid request
401Unauthorized
403Plan limit exceeded
404Not found
409Conflict (address taken)
500Server error

MCP Server (AI Agents)

Connect AI agents (Claude, Cursor, etc.) directly to your MX Forwarder account using the Model Context Protocol.

Local (stdio)

For Claude Desktop, Claude Code, Cursor:

json
{
  "mcpServers": {
    "mx-forwarder": {
      "command": "npx",
      "args": ["@mx-forwarder/mcp"],
      "env": { "MX_FORWARDER_API_KEY": "mf_live_your_api_key" }
    }
  }
}

Remote (OAuth)

For MCP clients with OAuth support:

MCP Endpointhttps://mx-forwarder.com/api/mcp
OAuth Discoveryhttps://mx-forwarder.com/.well-known/oauth-authorization-server

Available Tools

list_addresses— List forwarding addresses
create_address— Create a new address
update_address— Update address config
delete_address— Delete an address
list_emails— List and search emails
get_email— Get email details
retry_email_delivery— Retry failed delivery
list_deliveries— List delivery logs
get_usage— Get usage stats
list_sub_accounts— List reseller sub-accounts
create_sub_account— Create a sub-account
get_reseller_usage— Aggregate reseller usage

Reseller / White-Label

Build email-to-webhook functionality into your own SaaS product. The reseller plan lets you create sub-accounts for your customers, track their usage individually, and get billed for aggregate consumption.

How it works

  1. Contact us to get the reseller plan on your account
  2. Create sub-accounts via POST /api/reseller/sub-accounts with each customer's email
  3. Each sub-account gets an API key for programmatic access
  4. Sub-accounts can also sign up on the dashboard with the same email to get UI access
  5. Email volume counts against your reseller aggregate limit (1M emails/mo)
  6. Monitor per-customer usage via GET /api/reseller/usage

Example: create a sub-account

bash
curl -X POST https://mx-forwarder.com/api/reseller/sub-accounts \
  -H "Authorization: Bearer mf_live_your_reseller_key" \
  -H "Content-Type: application/json" \
  -d "text-amber-300">'{"email": "customer@acme.com"}'

"text-[#52525b]"># Response:
"text-[#52525b]"># { "sub_account": { "id": "01J...", "email": "customer@acme.com" },
"text-[#52525b]">#   "api_key": "mf_live_abc123..." }

Ready to get started?

Create your free account and set up your first address in under a minute.

Create free account