Skip to main content

Notification Scheduling

Schedule notifications for future delivery with time zone support, recurring patterns, and full lifecycle management.

Scheduled Delivery

Send at a specific date and time

Time Zone Support

Respect each user's local time

Recurring Patterns

Cron-based daily, weekly, monthly

Cancellation

Cancel or pause scheduled sends

Overview

Notification scheduling lets you queue notifications for future delivery instead of sending them immediately. Schedule one-time notifications for a specific moment, or set up recurring patterns like daily digests and weekly summaries. Combined with time zone support, you can ensure every user receives notifications at the right local time regardless of where they are.

Quick Start

Schedule a notification for future delivery with a single API call:

import { platform } from '@/lib/platform'

// Schedule a notification for a specific time
const schedule = await platform.notifications.schedule({
  userId: user.id,
  title: 'Weekly Summary',
  body: 'Your stats this week',
  sendAt: new Date('2026-02-07T09:00:00Z'),
  timezone: 'America/New_York',
})

// Returns a schedule ID for tracking and cancellation
console.log(schedule.id) // "sched_a1b2c3d4"
console.log(schedule.scheduledFor) // "2026-02-07T14:00:00Z" (adjusted for EST)

Time Zone Handling

When you set timezone: 'local', the platform automatically resolves each user's time zone from their profile. If no time zone is set for a user, it falls back to UTC.

Time Zone Support

Deliver notifications at the right time for each user, regardless of their location. The platform supports three timezone modes:

Explicit Time Zone

Specify an IANA time zone like America/New_York or Europe/London. The sendAt time is interpreted in that zone.

User Local Time

Set timezone: 'local' to use each user's stored time zone. A “9 AM” notification arrives at 9 AM local time for every user.

UTC (Default)

Omit the timezone field or set it to UTC. The notification sends at the exact UTC time specified.

// Explicit timezone - all users get it at 9 AM EST
await platform.notifications.schedule({
  userId: user.id,
  title: 'Meeting Reminder',
  body: 'Team standup in 15 minutes',
  sendAt: new Date('2026-02-07T09:00:00'),
  timezone: 'America/New_York',
})

// User-local timezone - 9 AM in each user's own timezone
await platform.notifications.schedule({
  userId: user.id,
  title: 'Good Morning',
  body: 'Start your day with a quick review',
  sendAt: new Date('2026-02-07T09:00:00'),
  timezone: 'local',
})

// UTC - sends at exactly 14:00 UTC regardless of user location
await platform.notifications.schedule({
  userId: user.id,
  title: 'System Maintenance',
  body: 'Scheduled maintenance window',
  sendAt: new Date('2026-02-07T14:00:00Z'),
})

Daylight Saving Time

The platform handles DST transitions automatically. A notification scheduled for “9 AM America/New_York” will correctly send at 9 AM EST or 9 AM EDT depending on the date.

Recurring Notifications

Set up recurring notifications with cron expressions. Recurring schedules continue until explicitly cancelled or paused.

// Daily digest at 9 AM in each user's timezone
await platform.notifications.scheduleRecurring({
  segment: 'active_users',
  title: 'Daily Digest',
  body: 'Here is your daily summary',
  cron: '0 9 * * *',
  timezone: 'local',
})

// Weekly summary every Monday at 10 AM UTC
await platform.notifications.scheduleRecurring({
  segment: 'all_users',
  title: 'Weekly Summary',
  body: 'Your week in review',
  cron: '0 10 * * 1',
  timezone: 'UTC',
})

// Monthly report on the 1st at 8 AM
await platform.notifications.scheduleRecurring({
  segment: 'premium_users',
  title: 'Monthly Report',
  body: 'Your monthly analytics report is ready',
  cron: '0 8 1 * *',
  timezone: 'local',
  // Optional: dynamic body template
  template: 'monthly_report',
})
Cron PatternDescription
0 9 * * *Daily at 9:00 AM
0 9 * * 1Every Monday at 9:00 AM
0 9 1 * *First day of each month at 9:00 AM
0 9 * * 1-5Weekdays at 9:00 AM
0 */6 * * *Every 6 hours
0 9,18 * * *Twice daily at 9:00 AM and 6:00 PM

Cron Format

Cron expressions use five fields: minute hour day-of-month month day-of-week. The minimum interval is 1 hour to prevent notification fatigue.

Cancellation

Cancel or pause scheduled notifications at any time. Both one-time and recurring schedules support full lifecycle management.

// Cancel a one-time scheduled notification
await platform.notifications.cancelScheduled(scheduleId)

// Cancel a recurring schedule (stops all future deliveries)
await platform.notifications.cancelScheduled(recurringId)

// Cancel all scheduled notifications for a user
await platform.notifications.cancelAllScheduled({
  userId: user.id,
})

Cancellation Window

Notifications in the delivery pipeline (within 60 seconds of their scheduled time) cannot be cancelled. Cancel well in advance to guarantee prevention.

Delivery Modes

One-time

Send at a specific date and time

sendAt: new Date("2026-02-07T09:00:00Z")

Recurring

Repeat on a cron schedule

cron: "0 9 * * *"

Time Zone Aware

Deliver at the right time for each user's locale

timezone: "America/New_York"

Immediate

Send right now (default behavior)

No sendAt required

Best Practices

Respect Quiet Hours

Avoid sending between 10 PM and 8 AM in the user's local time. Use timezone: "local" to ensure compliance.

Batch Recurring Sends

For recurring notifications to segments, the platform batches deliveries to avoid overwhelming your infrastructure.

Set Expiration Windows

Time-sensitive notifications should include an expiry so they are not delivered if the window passes.

Monitor Delivery Metrics

Track delivery rates, open rates, and failure counts to optimize send times and content.

Pause During Incidents

Use the pause API to halt non-critical recurring notifications during incidents or maintenance.

Use Templates for Recurring

Recurring notifications with dynamic content should use templates that resolve at delivery time.

API Reference

notifications.schedule()

PropertyTypeDescription
userIdrequiredstringTarget user ID for the notification
titlerequiredstringNotification title
bodyrequiredstringNotification body content
sendAtrequiredDateWhen to deliver the notification
timezonestring= "UTC"IANA timezone, "local" for user timezone, or "UTC"
dataRecord<string, unknown>Custom payload attached to the notification
expiresAtDateDo not deliver after this time. Useful for time-sensitive notifications.
channel"push" | "email" | "in_app" | "sms"= "push"Delivery channel

notifications.scheduleRecurring()

PropertyTypeDescription
segmentrequiredstringTarget user segment key
titlerequiredstringNotification title
bodyrequiredstringNotification body content
cronrequiredstringCron expression (5-field format, minimum 1 hour interval)
timezonestring= "UTC"IANA timezone, "local" for per-user timezone, or "UTC"
templatestringTemplate key for dynamic content resolved at delivery time
startsAtDate= nowWhen the recurring schedule becomes active
endsAtDateWhen the recurring schedule stops automatically

Management Methods

MethodDescription
notifications.cancelScheduled(id)Cancel a scheduled or recurring notification
notifications.cancelAllScheduled(opts)Cancel all schedules for a user
notifications.pauseSchedule(id)Pause a recurring schedule
notifications.resumeSchedule(id)Resume a paused recurring schedule
notifications.getSchedule(id)Get schedule details and delivery history
notifications.listSchedules(opts)List schedules with status and user filters