RAMSIO

Docs

API Contract

This is the exact contract the bundled apps/web expects from its backend — the cross-cutting mechanics that produce a confusing failure rather than a clean error when they’re wrong. Written for the frontend-only edition, but useful reading for anyone integrating against the API.

Origin & CORS

The frontend calls the API directly cross-origin — there is no same-origin proxy. Your backend needs CORS configured for the frontend’s origin with credentials enabled, since the refresh flow relies on a cookie.

Authentication flow

  • Access token: a short-lived JWT (15 minutes in this template’s own backend), returned in the JSON body of POST /auth/login and POST /auth/refresh, held in memory client-side — never in localStorage.
  • Refresh token: longer-lived (7 days), delivered as an httpOnly, Secure cookie, never in the response body. The cookie path must scope to your refresh endpoint exactly, or the browser silently never sends it.
  • CSRF: since the refresh endpoint is cookie-authenticated, check the Sec-Fetch-Site header (falling back to Origin) before honoring it.

Response shapes the frontend expects

User object

{
  "id": "string",
  "email": "string",
  "globalRole": "SUPER_ADMIN" | "ADMIN" | "USER",
  "hasPassword": true,
  "avatar": "string | null"
}

hasPassword is required — the account-deletion flow branches on it. Omitting it doesn’t error visibly; it silently routes every user down the wrong confirmation path.

Paginated list endpoints

{
  "data": [ /* ... */ ],
  "total": 0,
  "page": 1,
  "pageSize": 20,
  "totalPages": 0,
  "hasNextPage": false,
  "hasPreviousPage": false
}

A bare array here breaks every .filter()/.map() call on the response immediately.

Error responses

{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Human-readable message, or an array of validation messages"
}

Rate limiting

Not required for the frontend to function, but if you rate-limit auth endpoints (recommended), return a 429 with a Retry-After header in seconds — the frontend doesn’t parse a custom retry payload.

Full endpoint reference

This page covers integration mechanics, not every endpoint. For exact request/response shapes per route, reference the live Swagger UI generated at /api/docs once you have a full-stack or backend-only edition running — see Getting Started.