← All guides

Schedule emails in Next.js with Cuey and Resend

Keep email delivery in Resend and schedule your server-side handler through Cuey.

Schedule the handler that sends your email

Use Cuey to call a Next.js route at the chosen time. Use Resend inside that route to deliver the email. This pattern also works for recurring emails and callbacks that perform several actions.

For a single delayed email, Resend's built-in scheduling supports sends up to 30 days ahead. Use it for a delayed email within that window. Cuey is useful when you need recurring schedules or want to run your own endpoint first.

1. Configure your server

Install cuey and resend. Set CUEY_API_KEY, RESEND_API_KEY, SCHEDULE_WEBHOOK_SECRET, and EMAIL_FROM. Verify your sending domain with Resend.

2. Write the delivery route

This is an integration sketch. Implement loadEmailJob against your own database. Store the recipient and content server-side, and authorize the webhook before looking up the job.

// app/api/scheduled-email/route.ts
import { Resend } from "resend";

export async function POST(request: Request) {
  const secret = process.env.SCHEDULE_WEBHOOK_SECRET;
  if (!secret || request.headers.get("authorization") !== `Bearer ${secret}`) {
    return new Response("Unauthorized", { status: 401 });
  }

  const { email_job_id } = await request.json();
  // Load the job by ID from your database. Resolve recipient and
  // content server-side; check cancellation and current preferences.
  // This lookup is application-specific:
  const job = await loadEmailJob(email_job_id);
  if (!job || job.cancelled) return new Response(null, { status: 204 });

  const resend = new Resend(process.env.RESEND_API_KEY);
  const { error } = await resend.emails.send({
    from: process.env.EMAIL_FROM!, // Your verified sending domain
    to: job.recipient,
    subject: job.subject,
    text: job.text,
  }, { idempotencyKey: `scheduled-email/${job.id}` });

  if (error) return Response.json({ error: "Delivery failed" }, { status: 502 });
  return Response.json({ ok: true });
}

3. Schedule the route

import { cuey } from "cuey";

await cuey.schedule({
  webhook_url: "https://your.app/api/scheduled-email",
  method: "POST",
  scheduled_at: new Date(Date.now() + 900000).toISOString(),
  headers: {
    Authorization: `Bearer ${process.env.SCHEDULE_WEBHOOK_SECRET}`,
  },
  payload: { email_job_id: "email_job_123" },
});

Retries and recurring emails

A repeated callback must not send the same intended email twice. The example uses Resend's idempotency key for a one-off email job. Resend retains keys for 24 hours; use persistent deduplication in your database when your retry or replay window is longer. For recurring emails, create a new job and stable deduplication key for each intended occurrence.

Read Resend's send API and idempotency documentation before shipping.

Schedule your first request

The first 10,000 events each month are free. Additional events cost $0.0001 each. See pricing or read the API documentation.

Get your API key ↗