Run scheduled maintenance
Trigger recurring work and monitor its delivery queues.
Call /api/cron/tick every five minutes to process scheduled application work. Vercel configures this schedule through vercel.json.
Understand each maintenance run
A maintenance run processes these workflows:
- Due email, text-message, and push notifications
- Recurring-booking expansion, repriced against each visit's own date
- Card holds placed on bookings that have come within the authorization window, and stale holds replaced
- Expired provider offers, and waitlist offers nobody claimed — an unclaimed offer returns its request to the queue and passes the opening to the next priority band down
- Quote follow-up messages and quote expiry
- Lead follow-up messages, and the leads that have gone quiet
- Lead-score refresh
- Webhook fan-out and delivery retries
- Customer churn-score refresh
- Old idempotency-key and rate-limit-window cleanup
Each workflow is idempotent for its current state. Repeated invocations should not duplicate completed work.
Protect the maintenance route
Set a CRON_SECRET environment variable on the deployment. The route then serves only requests carrying that value as a bearer credential, and answers every other one 401 without running any work:
CRON_SECRET=a_long_random_value_hereVercel Cron reads the same variable and sends Authorization: Bearer <CRON_SECRET> with each scheduled call, so the schedule itself needs no change. Generate the value with openssl rand -hex 32.
Until CRON_SECRET is set, /api/cron/tick accepts GET and POST from anyone and warns on every invocation that it is open. Setting the variable is what closes it. An unset variable serves the request on purpose: refusing one instead would stop notifications and recurring bookings on deployments that had not set it yet, and nothing reports a schedule that has quietly stopped.
Keep the secret out of the cron URL and out of query strings, where proxies and access logs record it.
Trigger maintenance manually
Use the deployed application origin when diagnosing a delayed queue, and send the same credential the schedule sends:
curl --fail --silent --show-error \
--header "Authorization: Bearer $CRON_SECRET" \
"https://booking.example.com/api/cron/tick"The JSON response includes a count for each processed workflow. Check Vercel function logs when the request fails or a count remains unchanged despite due work. A 401 means the credential does not match the deployment's CRON_SECRET.
Check that the process is up
Point an uptime monitor or a load balancer at GET /api/ping. It is public and needs no credential, and it returns 200 with the body { "pong": true } and a Cache-Control: no-store header:
curl --fail --silent --show-error "https://booking.example.com/api/ping"/api/ping runs no query and reads no secret, so it answers 200 whenever the Node process is serving requests — even while the database is unreachable. Use it for a frequent "is the process up?" check. Use /api/health instead when you need to confirm the database is reachable and see the deployed revision; it returns 503 when the database check fails.
Read the server clock
Point drift detection at GET /api/time. It is public and needs no credential, and it returns 200 with the body { "utc": "<ISO 8601 UTC timestamp>" } (always UTC, ending in Z) and a Cache-Control: no-store header:
curl --fail --silent --show-error "https://booking.example.com/api/time"Like /api/ping, it runs no query and reads no secret, so it answers 200 even while the database is unreachable. Compare the returned utc against a caller's own clock to detect drift.
Inspect failed delivery queues
Use the application before retrying provider requests:
- Open
/admin/commsfor queued or failed email and text messages - Open
/admin/webhooksfor failed or dead webhook deliveries - Review Vercel logs for the latest
/api/cron/tickinvocation
Retry a failed delivery only after correcting its configuration or destination.