Skip to content

HTTP API

The site’s backend API is OpenAPI 3.0, and the specification is generated from the routes themselves rather than maintained beside them.

Every route lives under /api/v1/<tier>/, and the tier decides the gate.

Tier Routes Sits behind
admin ~93 noindex, isLoggedIn, requireRole('admin','editor','moderator')
auth ~38 Public by necessity; heavily rate-limited and bot-scored
player ~24 noindex, requireAuth — role-agnostic self-service
public ~19 Nothing, by design
settings 2 requireAuth + noindex, no role gate

Plus two outside the versioned surface: /api/health and /api/csp-report.

Those two are deliberately not under /api/v1. A browser learns the CSP report path from the policy header rather than from a client build, so it is not part of the versioned client contract.

player is role-agnostic. It is self-service for whoever is signed in, gated on requireAuth alone and never on “is not staff”. Staff are a superset of players — an administrator has characters too, and a player route that excluded them would 403 an admin off their own account.

A module’s routes inherit their tier’s gate and add their own on top. A module never re-implements the tier gate; see The module API.

Three ways in, one session model:

  • Cookie — httpOnly JWT, for the browser.
  • Bearer — short access tokens plus rotated, hashed, revocable refresh tokens, for the native app.
  • SSO — OAuth2/OIDC with PKCE, and link-only: an external identity must already be attached to an existing account.

Admin roles are re-validated against the database on every request, so a demoted user loses access immediately rather than at token expiry.

The uo-link sidecar exposes its own small REST and WebSocket surface, reached only by the website’s backend. It carries X-UOLink-Version and answers 409 on a protocol mismatch.

It is not part of this API and is not served from your site. See The bridge.

For contributors: the specification is generated from #swagger.* annotations next to each route, and the output is committed.

Terminal window
cd website/server && npm run swagger

A route that is not in the specification is not finished. Modules publish their own fragment, at a fixed filename in the bundle root, so a module’s routes appear in the same documentation as core’s.

BACKEND_DESIGN.md §4 is the API contract, including §4.0’s authoritative route list.