How admin access is granted
There's no UI to self-promote. A backend operator runs:
UPDATE profiles SET is_admin = TRUE WHERE id = <user_uuid>;After that the user can hit /admin. Every /api/v1/admin/* endpoint also re-checks is_admin server-side, so the page-level redirect is UX-only — security is at the API layer.
Queue tab
- Queue depth — count of
job_queuerows grouped by status (pending / running / done / failed / cancelled). Auto-refreshes every 10s. - Stuck jobs — rows in status=running with
claimed_at < NOW() - 10 min. Means a worker probably died mid-job. Worker startup runsreset_stuck_jobs(600)to recover automatically — this table shows what's currently stuck (would be reset on the next worker bounce). If it's non-empty during normal operation, investigate.
Costs tab
- Org spend today — sum across all users. The progress bar turns red when total > $50/day (the kill switch cap). At that point the ScholarFlow cron freezes for today.
- Top spending users — table of the top 5 users by today's cost. Each row shows their tier, today's spend, and their daily cap. Useful when you want to know who triggered a cost spike.
- Refreshes every 15s.
Audit tab
Last 50 rows from audit_log. Includes:
- Stripe webhook events (subscription created/canceled/updated)
- ScholarFlow cron skips (org cap hit)
- Bulk ingestion job results
- Worker stuck-job resets (with the IDs that were reset)
- Account deletions
Sortable + searchable. Refreshes every 15s.
What admin can't do yet
- Impersonate users (intentionally not built — auth scope is the auth scope)
- Ban a user account (the
profiles.banned_atcolumn exists, but no UI toggle yet) - Trigger a manual ScholarFlow cron (run via
POST /api/v1/cron/scholarflowwithX-Cron-Secret) - Re-process a failed job manually (delete + re-enqueue via SQL for now)
Direct DB access for admin queries
For anything not in the UI, hit Supabase Studio SQL editor with the service role key. Common admin queries (audit by user, cost-by-day timeseries, queue health snapshot) live in docs/admin-queries.md in the repo.