Scheduled Jobs
Every job, when it runs, what it does, how it authenticates, and how to run one by hand.
Daily Jobs on Vercel
Defined in vercel.json. Times are UTC. Each route requires Authorization: Bearer <CRON_SECRET>; Vercel adds it.
| Route | UTC | What it does |
|---|---|---|
/api/cron/onboarding-nudges | 11:00 | One profile nudge and one assessment nudge per person, ever, to members more than three days old |
/api/cron/lifecycle | 12:00 | Marks members dormant after 180 days and sends re-engagement; sends the digest on the first of every other month; idle mentor and volunteer nudges |
/api/cron/matching-engine | 12:30 | Syncs members to Nexus and proposes matches for ready founders |
/api/cron/send-invitations | 13:00 | Re-sends invitations for accepted applications that have none |
/api/cron/event-digest | 13:00 | Daily, weekly and monthly event digests |
/api/cron/match-notices | 13:30 | Closes expired matches, sends first notices and seven-day reminders |
/api/cron/match-checkpoints | 13:45 | The "how is it going" check-in after a match |
The Five-Minute Job on Supabase
/api/cron/event-reminders is called every five minutes by a Supabase pg_cron job through pg_net, because Vercel's plan allows daily jobs only. It sends event and session reminders, chases RSVPs and volunteers, marks sessions complete, and syncs attendance.
The schedule lives in the database, not in a migration file. select * from cron.job; shows it; select * from cron.job_run_details order by start_time desc limit 20; shows recent runs. If CRON_SECRET is rotated, the job's header must be updated too.
Run By Hand
/api/cron/nexus-outcomes is run by hand to resend match outcomes to Nexus; ?dry=1 lists what would be sent.
Any job can be triggered from a terminal that has the secret:
curl -H "Authorization: Bearer $CRON_SECRET" https://www.impacthubos.com/api/cron/lifecycleThe response is JSON saying what it did. Every job is safe to run twice: each records what it sent and skips it next time.
Writing a New Job
- Add
app/api/cron/<name>/route.ts. Check the bearer token first; return 401 without it. - Make it idempotent: record what was sent in a table keyed so a second run finds it.
- Log a summary at the end so Vercel's log shows what happened.
- Add the schedule to
vercel.json(daily or less) or topg_cron(more often). proxy.tsalready lists/api/cron(.*)as public, which means it skips the sign-in check, not that it is open.
Checking a Job Ran
Vercel, Settings, Cron Jobs shows each job's last run and result. A 401 means the secret on Vercel does not match what was sent; redeploy after setting it.

