API Routes Map & Audit

Last Updated: October 10, 2026 Scope: All route handlers under src/app/api/ — 84 endpoints.

CORE.

theunparty.app grew organically as a solo build, and the API surface is now large enough that it needs a single, shared picture before the user‑journey "game logic" gets wired on top of it. This document is that picture: what each route does, who calls it, how it's protected, and which routes are not wired to anything yet.

Every folder under src/app/api/<em></em> that contains a route.ts/route.tsx becomes a live HTTP endpoint (Next.js App Router). A [param] folder is a dynamic segment (e.g. book/[bookId]). Two files in the tree are not endpoints: book/[bookId]/route-old.ts (legacy, dead) and book/types.ts (shared types).

“This file is a hand‑maintained audit. When you add, remove, or re‑wire a route,”

“update the matching table and the Audit findings section.”

---

Visual map

How traffic flows: who calls the API → which domain handles it → where the data lives.

---

Legend

Wiring — is the route actually reached, and by whom?

Mark
🟢
Meaning
Called from the app UI (pages / components)

Auth — how the route is protected.

Mark
🌐
Meaning
Public — no auth

---

At a glance

Count
13
Primary store
Postgres (+ 1 in‑memory)

---

1. Journey engine (game logic)

The core loop the user journey is built on: steps (tasks), the questions and challenges attached to them, and founder onboarding. This is the surface the new game logic will wire into, so its gaps matter most.

Endpoint
/api/steps
Methods
GET POST PUT DELETE
Auth
👤
Purpose
List / manage the caller's steps (filter, paginate)
Wiring
🟢

¹ founders/responses is restricted to a single identity via FOUNDER_USER_ID, not a Clerk role.

---

2. Analytics & tracking

Records the journey so admins can see progress and retention. analytics/vercel is a drain endpoint and is listed again under Integrations.

Endpoint
/api/analytics/session
Methods
POST
Auth
👤
Purpose
Start / track a user session
Wiring
🟢

---

3. Admin console

Backs the /admin UI. Authorization here is inconsistent — see Audit finding B. 🛡️ = proper admin‑role check; ⚠️ = sign‑in only (any logged‑in user passes); ⛔ = no check at all.

Endpoint
/api/admin/articles
Methods
GET POST
Auth
🛡️
Purpose
List / create editorial articles
Wiring
🟢

---

4. Content

Editorial articles, stories, and the dynamic social image. articles/stories are public to read but require STORY_API_KEY to write or to pull the full external feed.

Endpoint
/api/articles
Methods
GET POST
Auth
🌐 / 🔑
Purpose
List published articles (GET); create (POST, key)
Wiring
🟢

---

5. Commerce

Legacy book sales (Stripe) plus the current Polar checkout / pricing path. webhook/polar also appears under Integrations.

Endpoint
/api/book
Methods
GET POST
Auth
🌐
Purpose
List books; create a book (Zod)
Wiring
🟢

---

6. UNPPP & build data

Read‑only data feeding the status dashboard and priority views. Three overlapping priority/effort endpoints exist — only priority-score is wired; see Audit finding A.

Endpoint
/api/unppp/priority-score
Methods
GET
Auth
🌐
Purpose
Live UNPPP priority scores (Prisma + percentiles, cached)
Wiring
🟢

² repo-data is unauthenticated but reads through server‑side GitHub App credentials.

---

7. Media & assets

SVG library management, generic uploads, Apple Music tokens, and cached brand assets. The SVG routes read/write the local filesystem, which is ephemeral on Vercel — see Audit finding C.

Endpoint
/api/get-svgs
Methods
GET
Auth
🌐
Purpose
List SVG files in public/svgs
Wiring
🟢

---

8. Messaging & search

Endpoint
/api/search
Methods
GET POST
Auth
🌐
Purpose
Hybrid search (local + theunpartyapi)
Wiring
🟢

---

9. Integrations (cron, webhooks, drains)

Machine‑to‑machine endpoints. These are supposed to have no UI caller — they are driven by Vercel Cron, external webhooks, or Vercel drains, and each is signed/secret‑gated.

Endpoint
/api/cron/db-cleanup
Methods
GET
Auth
⏰
Purpose
Prune analytics rows older than 30 days
Wiring
⏰

_Cron schedules live in vercel.json._

_Count note: webhook/polar and analytics/vercel are tallied under their primary domains (Commerce and Analytics) in At a glance and repeated here only to show the full machine‑to‑machine surface — so this table lists 9 rows for 7 unique Integrations‑owned endpoints._

---

Audit findings

A. Routes with no caller (orphans)

No fetch, navigation, or external trigger references these anywhere in the repo. Each is a decision: wire it into the journey, or delete it.

Endpoint
/api/steps/[stepId]/complete
Likely reason
Superseded by steps/check-completion (auto‑complete)
Suggested action
Confirm, then remove or wire as the manual override

_Not listed as orphans (intentionally UI‑less): cron/<em>, webhook/</em>, analytics/vercel, drains/traces (machine‑triggered); admin/questions/upload, admin/steps/sync-questions, music/mood-enrich (ops tools); stories (external API‑key feed)._

B. Authorization gaps & inconsistencies

Fixed. The /api/admin/* namespace used to mix three protection levels: admin/steps/[id]/visibility had no auth at all, and seven routes (dashboard-stats, db-stats, observability, questions, questions/[id], question-responses, repositories) checked sign-in but not the admin role, so any signed-up visitor could read admin data or change questions and repositories. The routes that did check returned 500, not 403, to non-admins.

Now every /api/admin/ handler starts with adminGuard() (src/lib/admin.ts): 401 when signed out, 403 when signed in but not an admin. proxy.ts also requires sign-in for /api/admin(.) as a backstop, but sign-in alone is never enough, since anyone can sign up. A new admin route must start with the guard too.

C. Legacy, duplicate & stub code

Item
Dead route file
Where
src/app/api/book/[bookId]/route-old.ts
Note
Superseded by route.ts; delete

D. Structural observations

Unauthenticated writes. The SVG/asset and book/score/chat routes accept

writes with no auth. Acceptable for a solo build, but each is a public write path — gate them (or at least rate‑limit) before launch.

Naming is mostly consistent — verb‑style top‑level SVG routes

(get-svgs, delete-svg, …) sit alongside RESTful nested resources (steps/[id], admin/…). Fine to leave, but new routes should prefer the nested RESTful form.

Priority/effort duplication. unppp/priority, unppp/priority-score, and

unppp/effort overlap; consolidating removes a class of "which one is live?" bugs.

---

Maintaining this map

There is no test runner in this repo (see CLAUDE.md); this audit was produced by static inspection. To refresh it after changing routes:

List endpoints & methods — every exported GET/POST/PUT/PATCH/DELETE

handler (as export async function, export function, or export const) under src/app/api/<em></em>/route.ts(x).

Check wiring — search the repo (outside src/app/api/) for the route path in

fetch(...), href/window.location, <Image>/<img> src, vercel.json crons, and provider webhook configs. No hit → likely an orphan (finding A).

Check auth — look for adminGuard(), auth(), STORY_API_KEY, CRON_SECRET,

or a signature verifier at the top of each handler.