Sending & Testing

Forward emails with MailBlastr Inbound

Receive inbound email on a verified domain, get the email.received webhook, then fetch the content and attachments and forward the message on with POST /emails.

Inbound lets you receive email with MailBlastr. This guide shows how to forward a received email to another address. It uses Next.js route handlers, but the same flow works in any framework — the only MailBlastr-specific parts are the webhook verification and the Receiving API calls.

  1. 1
    Verify a domain for receiving

    Add and verify a domain, then enable receiving on it. We strongly recommend verifying a subdomain (subdomain.example.com) rather than the root domain. See Managing domains and Custom domains.

  2. 2
    Create a POST route

    MailBlastr sends a webhook to your endpoint every time an email is received. Add a POST route that reads the raw request body.

  3. 3
    Create a webhook

    In the dashboard add a webhook pointing at your public HTTPS URL and subscribe to the email.received event. For local development, tunnel your localhost with a tool like ngrok and use that URL.

  4. 4
    Verify the webhook signature

    Copy the webhook signing secret (it starts with whsec_) and verify every request against the svix-id, svix-timestamp, and svix-signature headers — using the raw body. See Verifying webhooks.

  5. 5
    Process and forward

    On an email.received event, fetch the email content and attachments from the Receiving API, then forward the message with POST /emails.

When you enable Inbound on a domain, MailBlastr receives all mail sent to that domain (subject to MX priority). Verify a subdomain rather than your root domain so you do not capture mail meant for existing mailboxes.

The minimal POST route

Start by reading the raw request text — you need it both to verify the signature and to inspect the payload:

app/api/inbound-webhook/route.ts
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';

export async function POST(req: NextRequest) {
  try {
    const payload = await req.text();
    return NextResponse.json(JSON.parse(payload));
  } catch (error) {
    console.error(error);
    return new NextResponse(`Error: ${error}`, { status: 500 });
  }
}

Verify, fetch, and forward

The full handler verifies the Svix signature with your webhook secret, guards on the email.received event, fetches the email body and attachments from the Receiving API, base64-encodes each attachment, and forwards everything with the SDK.

Webhooks do not include the body, headers, or attachments — only metadata. You must call the Received email content and Attachments endpoints to retrieve them. This keeps payloads small for serverless environments with limited request-body sizes.
app/api/inbound-webhook/route.ts
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
import { Webhook } from 'svix';
import { MailBlastr } from 'mailblastr';

const mb = new MailBlastr(process.env.MAILBLASTR_API_KEY!); // a full_access key
const SECRET = process.env.MAILBLASTR_WEBHOOK_SECRET!; // starts with whsec_

export async function POST(req: NextRequest) {
  try {
    // 1. Verify the webhook — use the RAW body, never a re-stringified object.
    const payload = await req.text();
    const headers = {
      'svix-id': req.headers.get('svix-id') ?? '',
      'svix-timestamp': req.headers.get('svix-timestamp') ?? '',
      'svix-signature': req.headers.get('svix-signature') ?? '',
    };
    const event = new Webhook(SECRET).verify(payload, headers) as any;

    // 2. Only act on inbound mail.
    if (event.type !== 'email.received') {
      return NextResponse.json({ message: 'ignored' }, { status: 200 });
    }
    const emailId = event.data.email_id;

    // 3. Fetch the email content (HTML, text, headers).
    const { data: email } = await mb.emails.receiving.get(emailId);

    // 4. Fetch + base64-encode any attachments.
    const { data: list } = await mb.emails.receiving.listAttachments(emailId);

    const attachments = [];
    for (const a of list?.data ?? []) {
      const buf = Buffer.from(
        await fetch(a.download_url).then((r) => r.arrayBuffer()),
      );
      attachments.push({ filename: a.filename, content: buf.toString('base64') });
    }

    // 5. Forward the message. Replace from/to with your own addresses.
    const { data: forwarded } = await mb.emails.send({
      from: 'forwarder@yourdomain.com',   // an address on YOUR verified domain
      to: ['you@example.com'],            // where to forward it
      subject: event.data.subject,
      html: email?.html,
      text: email?.text,
      attachments,
    });

    return NextResponse.json({ forwarded });
  } catch (error) {
    console.error(error);
    return new NextResponse(`Error: ${error}`, { status: 500 });
  }
}
The forwarding from must be an address on a verified domain of yours — you cannot forward "as" the original sender. Use a reply_to header if you want replies to reach the original sender.

Test it

  1. Send a normal email to the domain you enabled — e.g. if you verified marketing.example.com, mail test@marketing.example.com.
  2. Try a plain HTML email, then one with one or more attachments.
  3. Confirm the forwarded copy arrives, and check the inbound email and webhook delivery in the dashboard.

When it works locally, deploy your app and add your production endpoint as a new webhook. See Forward emails and Reply to emails for related flows.