Vercel Email: How to Send Email from Vercel Functions
Send email from Vercel using Serverless Functions or Edge Functions. TypeScript examples, environment variables, cron jobs, and local testing. Works with or without Next.js.
To send email from Vercel, use an HTTP-based email API (not SMTP) in a Serverless or Edge Function. Vercel blocks outbound SMTP connections, so libraries like Nodemailer won't work — but any email API that sends over HTTP works perfectly. Create a function in the api/ directory, install an email SDK, and call the send method.
Quick setup:
npm install sendpigeon- Add
SENDPIGEON_API_KEYin Vercel project settings - Create a function in
api/and callpigeon.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 });
Why SMTP (Nodemailer) Doesn't Work on Vercel
Vercel Serverless Functions run in a sandboxed environment that blocks outbound SMTP connections (ports 25, 465, 587). If you try to use Nodemailer with an external SMTP server, your function will hang and eventually time out.
Symptoms:
sendMail()never resolves- Function times out after 10 seconds
ECONNREFUSEDorETIMEDOUTerrors
Fix: Use an email API that sends over HTTP (port 443) instead of SMTP. HTTP is not blocked on Vercel. Replace Nodemailer with an SDK like SendPigeon, Resend, or Postmark — all of which use HTTP under the hood.
// This does NOT work on Vercel:
const transport = nodemailer.createTransport({
host: "smtp.example.com",
port: 587, // Blocked
});
// This works on Vercel:
const pigeon = new SendPigeon(process.env.SENDPIGEON_API_KEY!);
await pigeon.send({ from, to, subject, html }); // Uses HTTP
Troubleshooting
Function times out when sending email
Most likely using SMTP (Nodemailer). Switch to an HTTP-based email API — see section above.
Email sends in development but not in production
Check that your SENDPIGEON_API_KEY environment variable is set for the Production environment in Vercel settings. By default, environment variables are only set for Development.
Emails land in spam
Set up DKIM, SPF, and DMARC on your sending domain. Without authentication, most providers will flag your email. Use our deliverability checker to verify your setup.
Cron job not triggering
Cron jobs only run on production deployments (not preview or development). Also verify your vercel.json cron schedule syntax and that the CRON_SECRET is set.
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
- Queue emails: How to Send Queued Email — patterns for background sending
- Set up DKIM, SPF, and DMARC on your sending domain
- Review the email deliverability checklist
- Build templates with our free visual email builder
- See the full Node.js SDK on GitHub
Other Platforms
Deploying elsewhere? We have guides for:
See all framework guides.