DocsCron Jobs
The 14 scheduled jobs and CRON_SECRET enforcement
Cron Jobs
Scheduled jobs are declared in vercel.json and implemented under src/app/api/cron/. All times UTC.
| Path | Schedule | Purpose |
|---|---|---|
/api/cron/notification-deliveries | * * * * * (every minute) | Drain the notification delivery queue with retry and backoff; dead-letter persistent failures |
/api/cron/reconcile-clips | */2 * * * * | Safety net that finalizes analysis jobs whose client tab closed |
/api/cron/session-starting-soon | */15 * * * * | One Starting soon push per rostered athlete shortly before a session |
/api/cron/training-session-reminders | 0 * * * * (hourly) | Remind athletes and coaches ~24 hours before a session |
/api/cron/usage-report | 0 2 * * * | Daily billing maintenance: expire lapsed trials, warn owners 3 days before a trial ends |
/api/cron/wearable-maintenance | 45 2 * * * | Wearable pipeline hygiene (staged uploads, orphaned samples) |
/api/cron/prune-activity | 0 3 * * * | Delete activity events older than the retention window |
/api/cron/notifications-cleanup | 30 3 * * * | Retention sweep of notifications and deliveries |
/api/cron/liveheats-sync | 0 7 * * * | Refresh LiveHeats-linked athletes, re-import active circuits, recompute qualification pathways |
/api/cron/liveheats-orgs-sync | 0 8 * * 2 (Tue) | Re-cache the athlete roster for every LiveHeats series a circuit references |
/api/cron/bsa-sync | 0 9 * * 1 (Mon) | Rebuild the BSA athlete cache |
/api/cron/admin-digest | 0 13 * * * | Daily operator summary email |
/api/cron/migration-drift | 30 13 * * * | Compare the repo's migration manifest with the live ledger |
/api/cron/review-nudge | 0 17 * * * | One reminder when a published guided review has sat unwatched |
Authorization
Every cron route calls isAuthorizedCron(request) (src/lib/cronAuth.js) first and returns 401 otherwise. It reads CRON_SECRET, fails closed when it is unset, and compares the Authorization: Bearer <secret> header in constant time. Cron routes use the service-role client, and each carries a Sentry monitor.
Run one manually
curl https://surflink.ai/api/cron/liveheats-sync -H "Authorization: Bearer $CRON_SECRET"