Receiving Email

Process inbound email

Configure your domain to deliver inbound email to Mailpipe, then read messages through the API or receive real-time webhook notifications as mail arrives.

MX Records

To receive email, point your domain's MX record at Mailpipe's inbound mail server. MX records tell the internet where to deliver email addressed to your domain.

DNS — MX record
Type:     MX
Host:     @             (root domain, e.g. example.com)
Priority: 10
Value:    mx.mailpipe.dev

# If you also want a subdomain to receive mail (e.g. mail.example.com):
Type:     MX
Host:     mail
Priority: 10
Value:    mx.mailpipe.dev

You can verify that the record has propagated using dig or an online MX lookup tool:

bash — verify MX record
dig MX example.com +short
# Expected output: 10 mx.mailpipe.dev.

Remove conflicting MX records

If your domain already has MX records pointing at another provider (e.g., Google Workspace, Microsoft 365), remove or replace them. Multiple conflicting MX providers will cause unpredictable delivery.

Mailbox Setup

A Mailpipe mailbox is an email address that stores inbound messages. Once your MX record points at mx.mailpipe.dev, create a mailbox for each address you want to receive mail at.

Create a mailbox in the dashboard

  1. Go to Inbox and click + New Mailbox
  2. Enter the local part (e.g., support, hello, billing)
  3. Select a verified domain from the dropdown
  4. Optionally set a display name and configure webhook delivery
  5. Click Create

Create a mailbox via API

cURL
curl -X POST "https://api.mailpipe.dev/v1/mailboxes" \
  -H "Authorization: Bearer mp_live_your-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "address": "support",
    "domain_id": "dom_xxxxxxxxxxxxxxxx",
    "display_name": "Support Team",
    "forward_to_webhook": true
  }'
Response
{
  "id": "mbx_01hx8yq3vdkj1p4nze7rbc56wf",
  "address": "support@example.com",
  "display_name": "Support Team",
  "forward_to_webhook": true,
  "created_at": "2025-01-15T10:30:00.000Z"
}

Mailpipe accepts mail for any mailbox you create. Mail delivered to an address that has no mailbox is rejected at the SMTP level with a 550 No such user response.

Webhooks

Webhooks let Mailpipe push a notification to your server the moment a message is received, eliminating the need to poll for new mail. Configure a webhook endpoint in the dashboard or via the API.

Registering a webhook endpoint

cURL — create webhook
curl -X POST "https://api.mailpipe.dev/v1/webhooks" \
  -H "Authorization: Bearer mp_live_your-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/api/webhooks/mailpipe",
    "events": ["message.received", "message.bounced"],
    "mailbox_ids": ["mbx_01hx8yq3vdkj1p4nze7rbc56wf"]
  }'

Webhook payload

When a message arrives, Mailpipe sends a signed POST request to your endpoint with the following payload:

Webhook payload
{
  "id": "evt_01hx9abc123",
  "event": "message.received",
  "created_at": "2025-01-15T10:30:00.000Z",
  "data": {
    "id": "msg_01hx8yq3vdkj1p4nze7rbc56wf",
    "mailbox_id": "mbx_01hx8yq3vdkj1p4nze7rbc56wf",
    "mailbox_address": "support@example.com",
    "from": "customer@example.com",
    "to": ["support@example.com"],
    "subject": "Need help with my order",
    "textBody": "Hello, I have a question about order #12345...",
    "htmlBody": "<p>Hello, I have a question...</p>",
    "attachments": [],
    "headers": {
      "message-id": "<abc123@example.com>",
      "date": "Mon, 15 Jan 2025 10:30:00 +0000"
    },
    "received_at": "2025-01-15T10:30:00.000Z"
  }
}

Verifying webhook signatures

Every webhook request includes an X-Mailpipe-Signature header. Verify this signature to confirm the request genuinely came from Mailpipe:

Node.js — signature verification
import crypto from 'crypto';

export function verifyWebhook(
  payload: string,     // raw request body as string
  signature: string,   // X-Mailpipe-Signature header value
  secret: string       // MAILPIPE_WEBHOOK_SECRET env var
): boolean {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Next.js App Router example
export async function POST(req: Request) {
  const payload = await req.text();
  const sig = req.headers.get('x-mailpipe-signature') ?? '';

  if (!verifyWebhook(payload, sig, process.env.MAILPIPE_WEBHOOK_SECRET!)) {
    return new Response('Unauthorized', { status: 401 });
  }

  const event = JSON.parse(payload);
  // Handle event.event === 'message.received' ...

  return new Response('OK', { status: 200 });
}

Your endpoint must return a 2xx response within 10 seconds. Failed deliveries are retried up to 5 times with exponential back-off. See the Webhooks guide for full retry semantics.

Message Storage

All inbound messages are stored in Mailpipe's Supabase-backed database and accessible via the REST API regardless of whether webhooks are configured.

Reading messages via API

cURL — list messages in a mailbox
# List the 20 most recent unread messages
curl "https://api.mailpipe.dev/v1/messages?mailbox_id=mbx_01hx8&unread=true&limit=20" \
  -H "Authorization: Bearer mp_live_your-api-key-here"
cURL — fetch a single message
curl "https://api.mailpipe.dev/v1/messages/msg_01hx8yq3vdkj1p4nze7rbc56wf" \
  -H "Authorization: Bearer mp_live_your-api-key-here"
Node.js SDK
const client = new MailpipeClient({ apiKey: process.env.MAILPIPE_API_KEY });

// List unread messages
const { data: messages } = await client.messages.list({
  mailbox_id: 'mbx_01hx8yq3vdkj1p4nze7rbc56wf',
  unread: true,
  limit: 20,
});

// Fetch full message
const message = await client.messages.get('msg_01hx8yq3vdkj1p4nze7rbc56wf');
console.log(message.subject, message.textBody);

// Mark as read
await client.messages.update('msg_01hx8yq3vdkj1p4nze7rbc56wf', { is_read: true });

Messages are stored for 90 days on the Free plan and 1 year on Pro and above. Attachments count toward your storage quota. See the Supabase integration guide to query message data directly from your own database client.

Polling vs. Webhooks

Mailpipe supports two patterns for consuming inbound messages. The right choice depends on your use case.

Polling

Periodically call GET /v1/messages to check for new messages. Simple to implement but introduces latency equal to your polling interval.

No public endpoint required
Works behind firewalls and NAT
Introduces delivery latency
Uses API quota even when no mail arrives

Webhooks

Mailpipe calls your endpoint the instant mail arrives. Real-time and efficient, but requires a publicly reachable HTTPS endpoint.

Near-instant notification
No wasted API calls
Automatic retries on failure
Requires public HTTPS endpoint

Recommendation

Use webhooks for production applications where low latency matters (e.g., customer support inboxes, automated reply systems). Use polling for scripts, batch processing, or development environments where you don't have a public endpoint. Many applications use both: webhooks for real-time processing and the polling API as a fallback to reconcile missed events.

Need Help?

Our team is here to help. Reach out if you have any questions.

Contact Support