Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Sichere ProgrammierungBroadcom erschwert den VMware-Ausstieg(22.09.2026 um 10:36 Uhr)
Sichere ProgrammierungWhat Did You Deliberately Leave Out of Your MVP?(22.09.2026 um 11:21 Uhr)
Sichere ProgrammierungThe Most Useful AI Feature Might Be a Better Question(22.09.2026 um 11:23 Uhr)
Sichere ProgrammierungBroadcom erschwert den VMware-Ausstieg(22.09.2026 um 10:36 Uhr)
Sichere ProgrammierungWhat Did You Deliberately Leave Out of Your MVP?(22.09.2026 um 11:21 Uhr)
Sichere ProgrammierungThe Most Useful AI Feature Might Be a Better Question(22.09.2026 um 11:23 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Building automated booking reminders with Vercel Cron Jobs and Resend

One of the most requested features in any booking platform is simple: "Send me a reminder before my appointment." It sounds trivial. But when you start thinking about the infrastructure — scheduled jobs, email queues, timezone handling —…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

One of the most requested features in any booking platform is simple:

"Send me a reminder before my appointment."



It sounds trivial. But when you start thinking about the infrastructure —

scheduled jobs, email queues, timezone handling — it gets complex fast.



Here's how I built automated booking reminders in Next.js 15 using

Vercel Cron Jobs and Resend, with zero additional infrastructure.







The problem with scheduled tasks in Next.js



Next.js is a request-driven framework. It handles HTTP requests — it doesn't

natively run background jobs on a schedule.



Traditional solutions involve:




  • A separate cron server (more infrastructure to manage)

  • A queue service like BullMQ or AWS SQS (overkill for most projects)

  • A third-party scheduler service (another paid dependency)



Vercel Cron Jobs solve this cleanly. They're available on all plans

including Hobby, configured in a single JSON file, and trigger your

existing API routes on a schedule.







Setting up Vercel Cron Jobs



Create a vercel.json in the root of your project:




{
"crons": [
{
"path": "/api/cron/reminders",
"schedule": "0 8 * * *"
}
]
}






0 8 * * * runs every day at 8:00 AM UTC. That's it — no additional

setup, no dashboard configuration beyond deploying your app.







Securing the endpoint



Vercel sends requests to your cron endpoint from their infrastructure.

You need to verify these requests come from Vercel and not from a random

external caller.



The standard approach: a shared secret in an Authorization header.




export async function GET(request: NextRequest) {
const authHeader = request.headers.get('authorization')

if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}

// proceed with the job
}






Set CRON_SECRET in your Vercel environment variables. Generate a

secure value with:




node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"












Finding tomorrow's bookings



The reminder logic is straightforward: find all confirmed bookings

that start tomorrow, then send an email for each one.




const tomorrow = new Date()
tomorrow.setDate(tomorrow.getDate() + 1)
tomorrow.setHours(0, 0, 0, 0)

const tomorrowEnd = new Date(tomorrow)
tomorrowEnd.setHours(23, 59, 59, 999)

const upcomingBookings = await db
.select()
.from(bookings)
.where(
and(
eq(bookings.status, 'confirmed'),
gte(bookings.startTime, tomorrow),
lte(bookings.startTime, tomorrowEnd)
)
)






Two things worth noting:



Only confirmed bookings. Pending bookings (waiting for payment

confirmation) are excluded. You don't want to remind someone about

an appointment that isn't actually confirmed yet.



Midnight boundaries. Setting setHours(0, 0, 0, 0) and

setHours(23, 59, 59, 999) ensures you capture the full day

regardless of what time the cron runs.







Sending the reminder email with Resend



For each booking, we fetch the related service, resource, and tenant

data, then send the reminder:




for (const booking of upcomingBookings) {
try {
const [service] = await db.select().from(services)
.where(eq(services.id, booking.serviceId)).limit(1)

const [resource] = await db.select().from(resources)
.where(eq(resources.id, booking.resourceId)).limit(1)

const [tenant] = await db.select().from(tenants)
.where(eq(tenants.id, booking.tenantId)).limit(1)

if (!service || !resource || !tenant) continue

const html = await render(
BookingReminderEmail({
customerName: booking.customerName,
businessName: tenant.name,
serviceName: service.name,
resourceName: resource.name,
date: formatDate(booking.startTime),
startTime: formatTime(booking.startTime),
endTime: formatTime(booking.endTime),
})
)

await resend.emails.send({
from: process.env.RESEND_FROM_EMAIL!,
to: booking.customerEmail,
subject: `Reminder: your appointment tomorrow at ${tenant.name}`,
html,
})

sent++
} catch (error) {
console.error(`Failed to send reminder for booking ${booking.id}:`, error)
failed++
}
}

return NextResponse.json({ success: true, sent, failed, total: upcomingBookings.length })






The try/catch per booking is intentional — if one email fails, the

loop continues and sends the remaining reminders. A single Resend

error shouldn't block everyone else's reminders.







The email template



Using React Email for the reminder template keeps things consistent

with the rest of the email system:




export function BookingReminderEmail({
customerName,
businessName,
serviceName,
resourceName,
date,
startTime,
endTime,
}: BookingReminderEmailProps) {
return (
<Html>
<Preview>Reminder: your appointment tomorrow at {businessName}</Preview>
<Body>
<Container>
<Heading>Reminder: appointment tomorrow 🗓️</Heading>
<Text>
Hi {customerName}, this is a reminder for your appointment tomorrow.
</Text>
<Section>
<Text><strong>Service:</strong> {serviceName}</Text>
<Text><strong>With:</strong> {resourceName}</Text>
<Text><strong>Date:</strong> {date}</Text>
<Text><strong>Time:</strong> {startTime} – {endTime}</Text>
</Section>
</Container>
</Body>
</Html>
)
}












Testing locally



Vercel Cron Jobs only trigger in production. For local development,

call the endpoint manually with the cron secret:



PowerShell:




$secret = (Get-Content .env.local | Select-String "CRON_SECRET").ToString().Split("=")[1]
Invoke-RestMethod -Uri "http://localhost:3000/api/cron/reminders" `
-Headers @{Authorization="Bearer $secret"}






curl:




curl -H "Authorization: Bearer YOUR_CRON_SECRET" \
http://localhost:3000/api/cron/reminders






The endpoint returns the count of sent and failed emails — useful

for verifying everything worked correctly.









What I learned



Vercel Cron Jobs are underrated. For most booking or scheduling

use cases, they're all you need. No queue infrastructure, no worker

processes, no additional cost.



Fail per item, not per batch. The try/catch inside the loop

pattern is important. A single failed email shouldn't abort the

entire reminder run.



Test with real data. Insert a booking with startTime = tomorrow

directly in your database and verify the email arrives before deploying.

Cron bugs are annoying to debug in production.






This reminder system is part of SlotKit — a production-ready

booking SaaS template for agencies built on Next.js 15, Supabase,

Stripe, and Resend.



If you're building booking functionality for clients and tired of

rebuilding the same infrastructure: slotkit.dev



Happy to answer questions in the comments.

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Building automated booking reminders with Vercel Cron Jobs and Resend

Thematisch verwandte Begriffe: Building, automated, booking, reminders · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-94426 | A vulnerability was determined in xuxueli xxl-job up to 3.5.0. The impac…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick