0012 — Vercel Cron allowed for web-app-native schedules
- Status: Superseded by ADR-0014 — the extraction work in A.3 removed the only motivation for the carve-out. ADR-0003 applies in full again.
- Date: 2026-04-23
- Deciders: Fede, Jose
- Amends: ADR-0003
Context
ADR-0003 established that every scheduled job runs on AWS EventBridge +
Lambda under agents/. The stated reason: scheduled work at that time was
entirely agent-side (scraping, SMS reminders, inbox polling). Per ADR-0001,
agent work doesn't run on Vercel.
The metric snapshot cron is a new case that doesn't fit that mold. It reads the dashboard's own endpoints, reuses ~700 lines of in-app computation, and writes one row per property to DynamoDB once per day. Putting it on a Lambda would require either (a) duplicating the computation into the Lambda bundle, or (b) self-calling back into the web app over HTTP — adding a deploy pipeline and SSM secrets for a job that is fundamentally web-app telemetry.
Decision
Two scheduling mechanisms, one rule per category.
| Category | Mechanism | Location | Example |
|---|---|---|---|
| Agent work (scraping, SMS, inbox, browser-agent triggers) | EventBridge + Lambda | agents/<name>/ |
listings-sync, tour-reminder |
| Web-app-native telemetry, report generation, in-app cron | Vercel Cron | src/app/api/cron/<name>/ |
metric snapshot |
The rule: if the scheduled job only reads and writes data the web app already has in-process — no browser automation, no external scraping, no SQS work — it can live on Vercel Cron. Everything else stays on EventBridge per ADR-0003.
What's on each today
Lambda + EventBridge (per ADR-0003):
| Job | Schedule | Lambda |
|---|---|---|
| Subscription renewal | rate(30 min) | agents/outlook-subscription-renewer |
| Tour reminder | rate(1 hour) | agents/tour-reminder |
| Listings sync | rate(15 min) | agents/listings-sync |
| Application link | one-shot on tour confirm | agents/application-link |
| Work order pipeline | rate(8 hours) | agents/wo-pipeline |
Vercel Cron (new):
| Job | Schedule | Route |
|---|---|---|
| metric snapshot | daily at 04:00 UTC | src/app/api/cron/snapshot-metrics/route.ts |
Consequences
Easier
- No new Lambda + deploy pipeline for the metric snapshot. Ships with
gh pr merge. - In-app computation stays in-app. No 700-line duplication, no HTTP self-call from Lambda.
Harder
- Two places to check when asking "what scheduled jobs do we have?" Mitigated by this ADR being the index.
- Discipline required: if a new job straddles the line, default to Lambda (the more isolated option).
Follow-up
- If Vercel Cron gains more than ~3 jobs, revisit whether a dedicated Cron Lambda would be simpler.
Alternatives considered
Move metric snapshot to Lambda per ADR-0003. Rejected. Requires either duplicating 700+ lines of dashboard computation into the Lambda bundle, or having the Lambda HTTP-call back into the web app on every run. Either way the Lambda is doing nothing the Next.js app can't do natively.
Supersede ADR-0003 entirely. Rejected. The agent/web decoupling (ADR-0001) still holds. Agent-side scheduled work still belongs on Lambda. ADR-0003's core principle is correct for that category; this ADR just adds a narrow carve-out it didn't anticipate.
Cron-triggered SQS message → Lambda consumer. Rejected. Adds queue indirection for a job that is fundamentally "once a day, write one row per property." No backpressure or retry semantics needed.