Back to blog
VercelEmailTutorialServerless

How to Send Email from Vercel

Send transactional email from Vercel Serverless Functions and Edge Functions. Complete guide with TypeScript examples, environment variables, cron jobs, and local testing.

SendPigeon TeamFebruary 15, 20266 min read

To send email from Vercel, create a Serverless Function in the api/ directory, install the SendPigeon SDK, and call pigeon.send(). This works with or without a framework — no Next.js required.

TL;DR

Quick setup:

  1. npm install sendpigeon
  2. Add SENDPIGEON_API_KEY in Vercel project settings
  3. Create a function in api/ and call pigeon.send()

Best for: Contact forms, transactional emails, scheduled digests, webhook-triggered emails.


Install the SDK

npm install sendpigeon

Set Up Environment Variables

Add your API key in the Vercel dashboard under Project Settings > Environment Variables:

SENDPIGEON_API_KEY=sp_live_your_key_here

For local development, create .env.local at your project root:

SENDPIGEON_API_KEY=sp_live_your_key_here

Or pull your Vercel environment variables locally:

vercel env pull .env.local

Add .env.local to .gitignore. Never commit API keys.


Send Email from a Vercel Serverless Function

Create a function in the api/ directory. Each file becomes an endpoint — api/send-email.ts maps to /api/send-email.

// api/send-email.ts
import { SendPigeon } from "sendpigeon";

const pigeon = new SendPigeon(process.env.SENDPIGEON_API_KEY!);

export async function POST(request: Request) {
  const { to, name } = await request.json();

  const { data, error } = await pigeon.send({
    from: "hello@yourdomain.com",
    to,
    subject: `Hey ${name}!`,
    html: `<h1>Welcome</h1><p>Thanks for signing up.</p>`,
  });

  if (error) {
    return Response.json({ error: error.message }, { status: error.status ?? 500 });
  }

  return Response.json({ emailId: data.id });
}

Using Next.js on Vercel? See our dedicated Next.js email guide — it covers Server Actions, API routes, and React Email.


Send Email from Vercel Edge Functions

Edge Functions run on V8 isolates closer to your users. The SendPigeon SDK works here because it uses the Fetch API — no SMTP or native Node.js modules.

// api/send-email-edge.ts
import { SendPigeon } from "sendpigeon";

export const config = {
  runtime: "edge",
};

const pigeon = new SendPigeon(process.env.SENDPIGEON_API_KEY!);

export async function POST(request: Request) {
  const { to, subject, html } = await request.json();

  const { data, error } = await pigeon.send({
    from: "hello@yourdomain.com",
    to,
    subject,
    html,
  });

  if (error) {
    return Response.json({ error: error.message }, { status: error.status ?? 500 });
  }

  return Response.json({ emailId: data.id });
}

Contact Form Example

A complete contact form handler with validation:

// api/contact.ts
import { SendPigeon } from "sendpigeon";

const pigeon = new SendPigeon(process.env.SENDPIGEON_API_KEY!);

export async function POST(request: Request) {
  const { name, email, message } = await request.json();

  if (!name || !email || !message) {
    return Response.json({ error: "Missing fields" }, { status: 400 });
  }

  // Notify yourself
  const { error } = await pigeon.send({
    from: "contact@yourdomain.com",
    to: "you@yourdomain.com",
    subject: `Contact form: ${name}`,
    html: `
      <p><strong>From:</strong> ${name} (${email})</p>
      <p><strong>Message:</strong></p>
      <p>${message}</p>
    `,
    replyTo: email,
  });

  if (error) {
    return Response.json({ error: "Failed to send" }, { status: 500 });
  }

  return Response.json({ success: true });
}

Schedule Emails with Vercel Cron Jobs

Vercel Cron Jobs trigger a GET request to your function on a schedule. Configure in vercel.json:

{
  "crons": [
    {
      "path": "/api/daily-digest",
      "schedule": "0 9 * * *"
    }
  ]
}
// api/daily-digest.ts
import { SendPigeon } from "sendpigeon";

const pigeon = new SendPigeon(process.env.SENDPIGEON_API_KEY!);

export async function GET(request: Request) {
  // Verify cron secret
  const authHeader = request.headers.get("authorization");
  if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
    return new Response("Unauthorized", { status: 401 });
  }

  // Fetch users and send digest
  const users = await getActiveUsers();

  await pigeon.sendBatch(
    users.map((user) => ({
      from: "digest@yourdomain.com",
      to: user.email,
      subject: "Your daily digest",
      html: `<h1>Hey ${user.name}</h1><p>Here's what happened today...</p>`,
    }))
  );

  return Response.json({ sent: users.length });
}

Cron jobs only run on production deployments. Set a CRON_SECRET environment variable — Vercel sends it as an Authorization header so your function can verify the request is legitimate.


Handle Errors

The SDK returns { data, error } instead of throwing:

const { data, error } = await pigeon.send({
  from: "hello@yourdomain.com",
  to: "user@example.com",
  subject: "Hello",
  html: "<p>Hello!</p>",
});

if (error) {
  console.error(`Email failed: ${error.message}`);

  if (error.status === 429) {
    // Rate limited — retry later
  }

  return Response.json({ error: error.message }, { status: error.status ?? 500 });
}

return Response.json({ emailId: data.id });

Test Locally

Test your emails locally before deploying:

npx @sendpigeon-sdk/cli dev

This starts a local email server. All emails are caught and viewable at localhost:4100. No emails reach real inboxes.

See our local email testing guide for full setup.


Frequently Asked Questions

Can I send email from Vercel without a framework?

Yes. Vercel supports standalone Serverless Functions without Next.js or any framework. Place a TypeScript file in the api/ directory and export a named HTTP method handler (like POST) — Vercel deploys it as a function automatically.

Does SendPigeon work on Vercel Edge Functions?

Yes. The SendPigeon SDK uses the Fetch API under the hood, so it runs in both the Node.js and Edge runtimes. Unlike SMTP-based libraries like Nodemailer, there are no native module dependencies to worry about.

How do I store my API key on Vercel?

Add SENDPIGEON_API_KEY as an environment variable in your Vercel project settings (Settings > Environment Variables). You can scope it per environment — Production, Preview, or Development. Access it in your function with process.env.SENDPIGEON_API_KEY.

Can I schedule emails on Vercel?

Yes. Use Vercel Cron Jobs to trigger a Serverless Function on a schedule. Define the schedule in vercel.json using cron syntax. The function receives a GET request and can send emails, digests, or reports on schedule.

What's the difference between Vercel Serverless and Edge Functions?

Serverless Functions run full Node.js with access to all npm packages. Edge Functions run on V8 isolates with Web Standard APIs — faster cold starts but no access to native Node.js modules like fs or net. Both work with SendPigeon since the SDK only uses fetch.


Next Steps


Other Platforms

Deploying elsewhere? We have guides for:

See all framework guides.