Docs
Everything you need to connect a project to Status, expose a working /api/health endpoint, and surface the results on your own site.
Troubleshooting & FAQ
Quick answers to the problems we see most. If you’re stuck, run curl -v against your endpoint and check the contract.
Setup errors
“No checks recorded yet” on a project page
The poller runs every 5 minutes. The first poll after you add a project may take up to one cycle. If you can’t wait, open /admin and hit Run poll now to trigger it immediately.
“Calendar unavailable”
The calendar uses the daily_status and day_counts RPCs from migration 0002_visualizations.sql. If you added the project before that migration was applied, paste the SQL in the Supabase SQL Editor and reload.
“Setup required” on the landing page
Means the Supabase env vars are missing or the service-role client can’t reach the DB. Verify NEXT_PUBLIC_SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY are set in your environment and the status app’s own Supabase project has the tables from the migrations.
Endpoint errors
The poller records site as down
Open the contract and check the four failure cases. Common causes:
- The URL is wrong, or the server is not reachable from Cloudflare.
- The endpoint returns HTML, an empty body, or a non-JSON error page.
- The endpoint takes more than 15 seconds (timeout).
- Body parses as JSON but has no results array.
401 / 403 on the public JSON API
These endpoints are public and don’t require auth. A 401 means you hit a different path — likely /api/poll (needs POLL_SECRET) or /api/projects (needs ADMIN_SECRET).
Component IDs not showing in the UI
The status page shows one row per id. If your response has duplicate ids, the latest wins (and the older ones are merged into the counts). Make the ids unique and stable — see the contract.
Auth / admin
I lost the admin key
Re-set ADMIN_SECRET in your env (local: .env.local; prod: Vercel project settings). Restart the dev server (or redeploy) so the change takes effect, then re-enter the key in the /admin unlock form.
“Unauthorized — the server rejected this key”
The admin key in the form does not match the server. Verify the env var, restart the process, and re-enter the key. The previous value is cleared from sessionStorage on every 401.
How auth headers work
| Endpoint | Header |
|---|---|
| POST /api/poll | x-poll-secret: $POLL_SECRET (or Authorization: Bearer $POLL_SECRET or x-admin-secret) |
| /api/projects, /api/projects/[slug] | x-admin-secret: $ADMIN_SECRET (or Authorization: Bearer $ADMIN_SECRET) |
| Everything else | Public — no header |
Timing and retention
| Question | Answer |
|---|---|
| How often is my endpoint polled? | Every 5 minutes, on a 5-minute schedule, from the Cloudflare cron Worker. |
| What timezone are times in? | UTC. The calendar groups by UTC day. |
| How long is history kept? | 370 days. The poller prunes checks older than that on every run. |
| Why do I have data for some components but not others? | A component only appears after its first poll. The project page unions “live” components with window stats so anything seen recently is visible. |
CORS
The poller is server-side (Cloudflare Workers), so CORS doesn’t apply to the actual health check. If you’re testing the public JSON API from the browser and need CORS, the app already returns permissive CORS headers for those read endpoints.
Still stuck?
- Re-read the contract with the failing response in front of you.
- Try the getting started checklist again.
- Open the Supabase SQL Editor and confirm the migrations applied cleanly.
- Check Cloudflare Worker logs for the cron run that produced the result you’re debugging.