Skip to main content
Resend Docs

Search documentation

Type to search this documentation.

On this pageOverview

Forward Received Emails

Received emails can also be forwarded to another email address.

The Node.js SDK provides a forward() helper method that simplifies forwarding received emails. This method automatically handles fetching the email content and attachments.

Here's how you can implement this:

Next.js
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
import { Resend } from 'resend';

const resend = new Resend('re_xxxxxxxxx');

export const POST = async (request: NextRequest) => {
  const event = await request.json();

  if (event.type === 'email.received') {
    const { data, error } = await resend.emails.receiving.forward({
      emailId: event.data.email_id,
      to: 'delivered@resend.dev',
      from: 'onboarding@resend.dev',
    });

    if (error) {
      return new NextResponse(`Error: ${error.message}`, { status: 500 });
    }

    return NextResponse.json(data);
  }

  return NextResponse.json({});
};
Rust
use axum::{
    extract::State,
    http::StatusCode,
    response::{IntoResponse, Json, Response},
};
use resend_rs::{
    types::{ForwardReceivingEmail, InboundEmailId},
    Resend,
};
use serde::Serialize;
use std::sync::Arc;

struct AppState {
    resend: Resend,
}

#[derive(Serialize)]
struct Empty {}

async fn example(
    State(state): State<Arc<AppState>>,
    Json(event): Json<resend_rs::events::EmailEvent>,
) -> Result<Response, Response> {
    if matches!(
        event.r#type,
        resend_rs::events::EmailEventType::EmailReceived
    ) {
        let opts = ForwardReceivingEmail::new(
            InboundEmailId::new(&event.data.email_id),
            "onboarding@resend.dev",
            vec!["delivered@resend.dev"],
        );
        let data = state.resend.receiving.forward(opts).await.map_err(|e| {
            (StatusCode::INTERNAL_SERVER_ERROR, format!("Error: {}", e)).into_response()
        })?;
        Ok(Json(data).into_response())
    } else {
        Ok(Json(Empty {}).into_response())
    }
}

By default, the forward method forwards the email in a way that preserves the original email content and attachments exactly as received.

Alternatively, you can forward emails as if they had been forwarded through an email client, with the forwarded message footer. For that, use passthrough: false and provide custom text or HTML content. The original email will be shown after the forwarded message footer:

Next.js
import { Resend, type EmailReceivedEvent } from 'resend';

const resend = new Resend('re_xxxxxxxxx');

export async function forwardEmail(event: EmailReceivedEvent) {
  const { data, error } = await resend.emails.receiving.forward({
    emailId: event.data.email_id,
    to: 'delivered@resend.dev',
    from: 'onboarding@resend.dev',
    passthrough: false,
    text: 'See attached forwarded message.',
    html: '<p>See attached forwarded message.</p>',
  });
}
Rust
use resend_rs::{
    events::EmailEvent,
    types::{ForwardReceivingEmail, InboundEmailId},
    Resend,
};

async fn forward(resend: &Resend, event: EmailEvent) {
    let opts = ForwardReceivingEmail::new(
        InboundEmailId::new(&event.data.email_id),
        "onboarding@resend.dev",
        vec!["delivered@resend.dev"],
    )
    .with_passthrough(false)
    .with_text("See attached forwarded message.")
    .with_html("<p>See attached forwarded message.</p>");

    let data = resend.receiving.forward(opts).await;
}

If you're not using Node.js or prefer not to use the forward helper method, you can manually forward received emails using the Send Email API.

The recommended approach is to download the raw email and parse it to properly extract content and attachments (especially for inline images). Then, you can send a new email with the extracted content and attachments using the Send Email API.

app/api/events/route.ts
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
import { Resend } from 'resend';
import { simpleParser } from 'mailparser';

const resend = new Resend('re_xxxxxxxxx');

export const POST = async (request: NextRequest) => {
  const event = await request.json();

  if (event.type === 'email.received') {
    // Get the email metadata
    const { data: email } = await resend
      .emails
      .receiving
      .get(event.data.email_id);

    // Download the raw email content if available
    if (!email?.raw?.download_url) {
      return new NextResponse('Raw email not available', { status: 500 });
    }

    const rawResponse = await fetch(email.raw.download_url);
    const rawEmailContent = await rawResponse.text();

    // Parse the raw email to extract content and attachments
    const parsed = await simpleParser(rawEmailContent, {
      skipImageLinks: true,
    });

    // Extract attachments with content_id for inline images
    const attachments = parsed.attachments.map((attachment) => {
      // Strip < and > from content IDs for proper inline image handling
      const contentId = attachment.contentId
        ? attachment.contentId.replace(/^<|>$/g, '')
        : undefined;

      return {
        filename: attachment.filename,
        content: attachment.content.toString('base64'),
        contentType: attachment.contentType,
        contentId: contentId || undefined,
      };
    });

    const { data, error } = await resend.emails.send({
      from: 'Acme <onboarding@resend.dev>',
      to: ['delivered@resend.dev'],
      subject: email.subject || '(no subject)',
      html: parsed.html || undefined,
      text: parsed.text || '',
      attachments: attachments.length > 0 ? attachments : undefined,
    });

    if (error) {
      return new NextResponse(`Error: ${error.message}`, { status: 500 });
    }

    return NextResponse.json(data);
  }

  return NextResponse.json({});
};
Suggest an edit

Propose a replacement for this page. The site team reviews it before applying any changes.

Export
Documentation menu