apps/web stack themselves. Integrating merchants never touch CRON_SECRET — they only receive signed webhooks (Webhooks).
How retries work
Failed webhook deliveries retry up to five times (1 min → 5 min → 30 min → 2 h → 24 h). Most of the time, retries run inline: whenever Payoes dispatches a new payment event, it processes any due retries first. That covers busy deployments without extra infrastructure. On a quiet deployment, a failed delivery might sit until the next payment event triggers a retry pass. To avoid that gap, callPOST /api/cron/webhook-retries on a schedule.
CRON_SECRET
Set a bearer token in apps/web/.env.local or your production environment:
401.
You don’t need a scheduler for local dev. To smoke-test the endpoint:
CRON_SECRET set, the route returns 503.
Production endpoint
X-Cron-Secret works as an alternative to the Authorization header.
Response:
processed is how many due deliveries were attempted in that run (capped at 25).
Scheduling
Next.js doesn’t run background jobs. Pick a scheduler and hit the endpoint every 1–5 minutes.Vercel Cron
Addvercel.json at the repository root (or app root, depending on your Vercel project):
CRON_SECRET to your Vercel environment variables. Some Vercel plans don’t attach Authorization automatically — if auth fails, trigger the endpoint from GitHub Actions or another scheduler instead.
GitHub Actions
CRON_SECRET in repository secrets and PAYOES_URL in repository variables.
Other schedulers
cron-job.org, Railway cron, Render cron, or a plaincrontab on a VPS all work — any service that can POST with the bearer token.
Troubleshooting
| Problem | What to check |
|---|---|
503 CRON_SECRET is not configured | Env var missing in the deployment |
401 Unauthorized | Token mismatch or wrong header |
| Retries stall in production | No scheduler and no payment events to trigger inline processing |
processed: 0 | Nothing was due at that moment |