REST Surface
A short tour of the contract. Full schema bodies live in the repository-root openapi.json.
| Path | Method | Auth | Purpose |
|---|---|---|---|
/ |
GET | none | Health check |
/items |
GET | none | List items (public) |
/items |
POST | session | Create item (caller becomes createdBy) |
/items/:id |
PATCH | session + owner/admin | Update |
/items/:id |
DELETE | session + owner/admin | Delete |
/blog/posts |
GET | none | Paginated published posts (offset, limit, optional tag) |
/blog/posts/:slug |
GET | none | One published post (bumps view count) |
/blog/posts/:slug |
HEAD | none | Existence check, no view-count bump (backs the proxy's 404 gate) |
/blog/tags |
GET | none | Distinct tags |
/users/me/delete-account |
POST | session | Queue account deletion (worker ghosts asynchronously) |
/admin/users |
GET | session + admin | Paginated user list |
/admin/users/:id |
PATCH | session + admin | Grant or revoke admin ({ isAdmin }); refused for your own row |
/admin/blog/posts |
GET/POST/PATCH/DELETE | session + admin | Full blog CRUD (drafts included) |
/admin/email/stats |
GET | session + admin | Aggregate email queue stats |
/admin/email/campaigns/:id |
GET | session + admin | Per-campaign breakdown |
/internal/auth-adapter/* |
varies | X-Internal-Token |
NextAuth adapter routes, plus the magic-link rate-limit check (excluded from OpenAPI) |
/billing/status |
GET | Public | Whether billing is configured on this deploy |
/billing/me, /billing/checkout, /billing/portal |
GET/POST | Session | Entitlement + Stripe Checkout/Portal redirects |
/webhooks/stripe |
POST | Stripe signature | Billing webhook (raw body; excluded from OpenAPI) |
/admin/mcp |
POST | API key | Stateless MCP transport (excluded from OpenAPI; GET/DELETE → 405) |
Pagination is uniform: ?offset=0&limit=50 with hard cap 200. Responses for list endpoints are { items: T[]; total: number }.
Guards
Three guards: SessionGuard (src/auth/auth.guard.ts, Bearer token → req.user), AdminGuard (src/admin/admin.guard.ts, stacked after it, reads req.user.is_admin), and InternalApiTokenGuard (src/auth/internal-api-token.guard.ts, X-Internal-Token). Behavior and stacking order: Auth Flow.