Local scheduling
Mnueron ships two scheduled jobs: Granola sync (every ~10 min) and the nightly archive worker (once daily). Vercel Hobby caps you at one cron slot, so we recommend triggering both from a local scheduler instead. Same outcome, zero cost, no upgrade pressure.
The mental model
The deployed app exposes two cron-style endpoints that accept an x-cron-secret header. Anything that can send an HTTP GET with one header can be your scheduler — Windows Task Scheduler, macOS launchd, plain cron, GitHub Actions, even a Raspberry Pi on your desk.
GET /api/integrations/meet/granola/sync— pulls new Granola notes for every org.GET /api/cron/archive— runs the archive worker for every org with a configured backend.
One-time setup
- Generate a strong CRON_SECRET. 32+ random characters. PowerShell:
$bytes = New-Object byte[] 32 [Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes) [Convert]::ToBase64String($bytes) - Set CRON_SECRET in Vercel. Project Settings → Environment Variables → add for Production. Redeploy so it takes effect.
- Set CRON_SECRET in your local .env. Same value. Used by the scripts in
scripts/sync-granola.mjsandscripts/archive-run.mjs. - Set SYNC_TARGET_URL in your local .env. Default is
http://localhost:3111(local dev). For production scheduling, set it to your deployed URL (e.g.,https://www.mnueron.com).
# C:\Mnueron\ai-boilerplate-pro\.env
CRON_SECRET=<the-base64-value>
SYNC_TARGET_URL=https://www.mnueron.comManual run (anytime)
cd C:\Mnueron\ai-boilerplate-pro
npm run sync:granola
npm run archive:runEach script prints what it did: how many notes ingested, how many duplicates, any errors. Useful for verifying everything works before automating.
One-off against localhost instead of production:
npm run sync:granola -- --url http://localhost:3111Windows Task Scheduler — every 10 minutes
- Open Task Scheduler → Create Task… (not "basic task" — you need the repeat-every-N options).
- General tab: name "Mnueron Granola Sync", check "Run whether user is logged on or not".
- Triggers tab → New:
- Begin:
On a schedule - Settings: Daily, start time today 12:00 AM, recur every 1 day
- Advanced: Repeat task every 10 minutes for a duration of 1 day
- Stop task if runs longer than: 5 minutes
- Begin:
- Actions tab → New:
- Action: Start a program
- Program/script:
npm - Arguments:
run sync:granola - Start in:
C:\Mnueron\ai-boilerplate-pro
- Conditions tab: optionally uncheck "Start only on AC power" so it runs on battery too.
- OK → enter your password when prompted. First run validates everything.
Archive job — once a day at 3am
Same flow as Granola sync, but a different schedule:
- Task name:
Mnueron Archive - Trigger: Daily at 3:00 AM. No repeat needed.
- Action:
npmwith argumentsrun archive:run - Start in:
C:\Mnueron\ai-boilerplate-pro
mac / linux cron
If you'd rather use real cron:
# Granola every 10 min
*/10 * * * * cd /path/to/ai-boilerplate-pro && /usr/local/bin/npm run sync:granola >> /var/log/mnueron-granola.log 2>&1
# Archive nightly at 3am
0 3 * * * cd /path/to/ai-boilerplate-pro && /usr/local/bin/npm run archive:run >> /var/log/mnueron-archive.log 2>&1Use the full path to npm (find with which npm) because cron's PATH is minimal.
Security note
The CRON_SECRET is the only thing protecting the cron endpoints from unauthenticated traffic. Treat it like a password: generate a long random value, never commit it, rotate if you suspect leakage. If you ever see suspicious POST volume on these endpoints in Vercel logs, rotate immediately.