# HTTP API All backend routes live under `/api/` and are served by FastAPI. In production, the browser hits them same-origin via a Next.js rewrite; in dev they're at `http://localhost:8000/api/...`. Full auto-generated docs are always at `http://localhost:8000/docs` (Swagger UI). ## Public endpoints (no auth) ### Health / readiness | Method | Path | Response | |---|---|---| | GET | `/api/health` | `{status, data_dir, data_dir_exists}` — always 200 if process is up | | GET | `/api/ready` | `{ready, checks, bills_count}` — 200 if data is loaded, 503 otherwise | ### Bills | Method | Path | Notes | |---|---|---| | GET | `/api/bills` | List bills. Query: `state`, `status`, `session_year`, `category`, `search`, `limit`, `offset` | | GET | `/api/bills/stats` | Totals: `{total_bills, federal_bills, state_bills, most_active_state, last_updated_at}` | | GET | `/api/bills/filters` | Dropdown data: `{states, statuses, years, categories}` | | GET | `/api/bills/{bill_id}` | Full bill detail (used by the row-detail modal) | | GET | `/api/bills/by-state` | `{STATE: count}` for the choropleth. Optional `?year=YYYY` | | GET | `/api/bills/export.csv` | Filtered CSV download (same filters as `/api/bills`) | ### Insights | Method | Path | |---|---| | GET | `/api/insights/by-state?limit=N` | | GET | `/api/insights/by-category?limit=N` | | GET | `/api/insights/over-time` | ### Calendar | Method | Path | Notes | |---|---|---| | GET | `/api/calendar` | Month view. Query: `year`, `month`, `types[]` | | GET | `/api/calendar/upcoming` | Next N days. Query: `days` (default 7, max 30) | ### Newsletter | Method | Path | Notes | |---|---|---| | GET | `/api/newsletters` | List available dates | | GET | `/api/newsletters/{date}` | Markdown of one newsletter | | GET | `/api/newsletters/{date}/changes` | Weekly changes CSV rows for that date | | GET | `/api/newsletters/{date}/digest` | Stats + in-motion bills for the hero + Bills in Motion sections | | GET | `/api/newsletters/{date}/featured-bill` | "Bill of the Week" — scored pick from the week's changes | | GET | `/api/newsletters/{date}/category-trends` | Top IAPP categories affected this week | ### States | Method | Path | |---|---| | GET | `/api/states/featured` | | GET | `/api/states/{state}/spotlight?key_bills=N` | ### Feedback / subscribe | Method | Path | Body | |---|---|---| | POST | `/api/feedback` | `{name?, email?, type, description}` | | POST | `/api/newsletter/subscribe` | `{email}` | | POST | `/api/newsletter/unsubscribe` | `{email}` | ### AI tools | Method | Path | Body | |---|---|---| | GET | `/api/ai/bills/list` | Bills available in the AI tools dropdown | | GET | `/api/ai/bill/{state}/{bill_number}` | Bill + summary + suggested questions + has_report flag | | GET | `/api/ai/report/{state}/{bill_number}` | Pre-generated report markdown | | POST | `/api/ai/qa` | `{state, bill_number, question}` | | POST | `/api/ai/compare` | `{focus_state, focus_bill_number, compare[], question}` | | POST | `/api/ai/eu-compare` | `{state, bill_number, question}` | ## Auth | Method | Path | Notes | |---|---|---| | POST | `/api/auth/login` | `{username, password}` → `{token, username, name}` | | GET | `/api/auth/me` | Current user; requires `Authorization: Bearer ` | | POST | `/api/auth/logout` | Client discards the token | ## Admin endpoints All under `/api/admin/*`. Require `Authorization: Bearer ` where the JWT was issued by `/api/auth/login`. Enforced by the `require_admin` dependency in every admin route. ### Overview | Method | Path | Notes | |---|---|---| | GET | `/api/admin/overview` | Card counts, including `new_feedback_count` (fed to the nav badge) | ### Users | Method | Path | Body | |---|---|---| | GET | `/api/admin/users` | List | | POST | `/api/admin/users` | `{username, name, email, password}` | | PATCH | `/api/admin/users/{username}` | `{name?, email?, password?}` (any subset) | | DELETE | `/api/admin/users/{username}` | | ### Subscribers | Method | Path | |---|---| | GET | `/api/admin/subscribers` | | DELETE | `/api/admin/subscribers/{email}` | ### Feedback | Method | Path | Body | |---|---|---| | GET | `/api/admin/feedback` | | | PATCH | `/api/admin/feedback/{id}` | `{status}` — one of `new`, `read`, `resolved` | | DELETE | `/api/admin/feedback/{id}` | | ### Pipeline | Method | Path | Body / Notes | |---|---|---| | GET | `/api/admin/pipeline/status` | Current or last run | | GET | `/api/admin/pipeline/history?limit=N` | Last N runs | | GET | `/api/admin/pipeline/scripts` | The 14 step list with names + indices | | GET | `/api/admin/pipeline/log?tail=N` | Last N lines of the pipeline log | | POST | `/api/admin/pipeline/run` | `{pull?, test?, test_count?, start_from?, stop_after?, reset_vectorstore?, upload_to_hf?}` (all optional) | | POST | `/api/admin/pipeline/cancel` | Kills the running subprocess group | | POST | `/api/admin/pipeline/rebuild-manifest` | Standalone `rebuild_vectorstore_manifest.py` run | ### Weekly changes | Method | Path | |---|---| | GET | `/api/admin/weekly-changes` | | GET | `/api/admin/weekly-changes/{week_of}` | ## Error format Errors follow FastAPI's default `{"detail": "..."}` shape. Server-side 5xx errors also include a `request_id` — a short hex string echoed in the `X-Request-ID` response header. When a user reports an error, ask for the request ID and grep the backend logs for `rid=`. ## Rate limiting None currently. Add one at the ingress layer (HF's edge) if traffic ever warrants it.