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/addressesList all forwarding addresses
POST/api/addressesCreate a new forwarding address

Request Body (JSON)

local_partstringLocal part of the email (e.g. "invoices"). Random if omitted.
webhook_urlstringURL 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
simulationbooleanEnable test mode (no actual forwarding)
store_attachmentsbooleanStore 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_urlstringNew webhook URL
webhook_format"json" | "raw"Payload format
webhook_method"POST" | "GET" | "PUT" | "PATCH"HTTP method
attachment_mode"url" | "inline"Attachment delivery mode
simulationbooleanTest mode
store_attachmentsbooleanR2 storage
is_activebooleanEnable/disable the address
DELETE/api/addresses/{id}Delete an address and all associated data

Emails

GET/api/emailsList received emails with pagination and filtering

Query Parameters

qstringFull-text search query
address_idstringFilter by address ID
limitnumberResults per page (default 50, max 100)
cursorstringPagination 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}/rawDownload original RFC822 .eml file (binary, not JSON)
POST/api/emails/{id}/retryRe-queue email for webhook delivery

Deliveries

GET/api/deliveriesList webhook delivery logs

Query Parameters

address_idstringFilter by address ID
email_idstringFilter by email ID
status"pending" | "delivered" | "failed"Filter by status
limitnumberResults per page (default 50, max 100)
cursorstringPagination cursor

Response

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

Usage

GET/api/usageCurrent 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-accountsList 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-accountsCreate sub-account. Email is an identifier (can be synthetic). Returns API key (once).

Request Body (JSON)

emailstringrequiredIdentifier (can be synthetic, e.g. feeder-{userId}@your-domain.com)
planstringPlan for address/retention limits (default: "free")
external_idstringYour 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)

planstringNew plan for the sub-account
external_idstringUpdate your customer reference
DELETE/api/reseller/sub-accounts/{id}Delete sub-account and all its data
GET/api/reseller/usageAggregate usage across all sub-accounts

Query Parameters

monthstringBilling 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_addressesList forwarding addresses
create_addressCreate a new address
update_addressUpdate address config
delete_addressDelete an address
list_emailsList and search emails
get_emailGet email details
retry_email_deliveryRetry failed delivery
list_deliveriesList delivery logs
get_usageGet usage stats
list_sub_accountsList reseller sub-accounts
create_sub_accountCreate a sub-account
get_reseller_usageAggregate 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