Denis Cercasin

Reference documentation

Table of contents

Conventions

  • Base URL: http(s)://<host>:<PORT><ORIGIN_PATH> — locally http://localhost:8080/api, production https://roomofhorror.de/api.
  • Parameters are passed as query parameters; request bodies are not used.
  • Authentication: protected endpoints expect a JWT access token in the Authorization header (raw token, no Bearer prefix). Unity endpoints authenticate with the deviceUUID query parameter instead.
  • Validation: every request is validated by a Zod schema (schemas/requests.ts); invalid input returns 400 with a description before any handler logic runs.
  • Rolling tokens: authenticated game endpoints return a fresh accessToken with each successful response.
  • DEV_MODE: when the API runs with DEV_MODE=true, login/signup/recovery responses additionally contain the token that would normally only be sent by e-mail (authToken / recoveryToken) — this enables local development without an SMTP server.
  • A ready-to-use Postman collection ships in the repository: room-of-horror-api/roh_2_1.postman_collection.json.

All error responses share the shape { "description": "<human readable message>" } (messages in messages/APIMessages.ts). Unexpected failures are logged to the errors table and return 500 with an error reference.

Account endpoints

Method & path Auth Parameters Purpose
POST /account/signup – (rate limited) email, givenName, surname, address Creates an account, sends signup mail with auth link. 409 on duplicate e-mail
POST /account/login – (rate limited) email Sends login mail with auth link; returns one-time sessionID. 404 unknown e-mail, 408 cooldown active
GET /account/auth Auth token (header) Redeems the e-mailed auth token (single-use, blacklisted) and activates the account. Returns accessToken. 401 invalid/used token
GET /account/use-session-id sessionID Polls the one-time session ID created at login; once the mail link was clicked, returns accessToken. 401 invalid/expired
POST /account/get-recovery – (rate limited) email Sends recovery mail. 404 unknown e-mail, 408 cooldown
GET /account/set-recovery Recovery token (header) Redeems recovery token, rotates auth_uuid (invalidates all old tokens), returns fresh accessToken
GET /account/retrieve Access token (header) type = check | user | game Returns account validity, profile data, or the joined game

Example — login flow in development:

curl -X POST "http://localhost:8080/api/account/login?email=test@example.org"
# → { "description": "...", "sessionID": "...", "authToken": "<only in DEV_MODE>" }

curl -H "Authorization: <authToken>" "http://localhost:8080/api/account/auth"
# → { "description": "...", "accessToken": "<JWT>" }

Game endpoints

Method & path Auth Parameters Purpose
GET /game/get-game Access token or deviceUUID deviceUUID (optional) Returns the full game state. The user variant additionally contains found errors, coordinates, mistake positions, the decrypted deviceUUID, the live evaluation object and a fresh accessToken. 404 no game
POST /game/join-game Access token gameCode Links the logged-in user to the game shown in the VR lobby. 404 unknown code or already in use
PUT /game/edit-game Access token target, value Universal game mutation — see targets below. 400 invalid value or game already running
POST /game/error-found deviceUUID errorID Marks an error as found. Ends the game (409 + evaluation) when it was the last one. 400 not selected / already found, 409 mistake budget exhausted
POST /game/add-mistake deviceUUID mistakeCor = [x,y,z] Records a wrong click; ends the game when the mistake budget is reached
POST /game/stop-game deviceUUID Ends a running game from the headset and persists the evaluation. 409 if not running
POST /game/create-game deviceUUID errorPos Creates a game row manually (normally done by device-init)

edit-game targets

target value Validation
status LOBBY | SETUP | STARTED | ENDED Starting resets found errors and mistakes
errors JSON array of error IDs, e.g. [1,23,24] Each ID must be allowed for the game’s current patient
mistakes Number ≤ MAXIMUM_MISTAKE_AMOUNT Mistake budget (health bar)
patientId patient_apatient_f Also sets the matching patientModel; rejected while a game is running
patientModel Unity model key Direct model override; rejected while a game is running
uuid Sign-out: unlinks the user from the game

Patient endpoint

Method & path Auth Purpose
GET /patient/get-patients Access token Returns all active patient profiles (demographics, medical master data, accent color, allowedErrorIds, defaultErrorAmount) for the selection UI

Device endpoints (Unity)

Method & path Auth Parameters Purpose
POST /device/device-init deviceUUID errorPos Headset bootstrap: creates the game on first contact or reloads it (status LOBBY/SETUP depending on linked user); returns the gameCode. Updates stored error positions
POST /device/update-error-positions deviceUUID errorPos Re-sends all error world positions after the room was configured for a patient

errorPos format: JSON array of [errorId, x, y, z] tuples generated by Unity’s ErrorObject.getAllErrorCor().

WebSocket (Socket.IO)

  • Path: <ORIGIN_PATH>/socket.io on the same server; CORS restricted to the frontend origins.
  • Join: emit joinGameRoom with { token } (frontend) or { deviceUUID, clientType: "vr" }; the server resolves the game and joins the socket to room session:<game_uuid>.
Direction Event Payload highlights
client → server joinGameRoom, clientReady Join payload; ack returns the current GameSocketStatePayload
client → server requestGameState Re-sync request
client → server leaveGameRoom Leaves the room
server → client gameStateUpdated Full state: status, patient, errors, found errors, coordinates, mistakes, live evaluation
server → client errorFound errorId + full state
server → client mistakeMade mistakeCoordinates + full state
server → client gameStarted / gameEnded Lifecycle + full state
server → client playerJoined/Left, deviceConnected/Disconnected Presence with clientType
server → client reconnectRequired, error { code, message, timestamp }

Type definitions for every payload: room-of-horror-api/websocket/Events.ts (mirrored in room-of-horror-frontend/src/services/gameSocket.events.ts).

Environment variables

API (room-of-horror-api/.env.dev / .env.prod)

Validated at startup by utils/Config.ts; missing or malformed values abort with [CONFIG] Invalid environment configuration. See CONFIGURATION.md for secret generation commands.

Group Variables Notes
Mode USE_ENV, DEV_MODE DEV_MODE=true disables HTTPS and exposes dev-only token responses
JWT AUTH_KEY/EXP, ACCESS_KEY/EXP, RECOVERY_KEY/EXP Secrets ≥ 32 bytes, durations like 15min, 60d
Session SESSION_ID_LENGTH, SESSION_ID_VALIDITY_SPAN One-time login handover
Server PORT, ORIGIN_PATH, FRONTEND_ORIGIN, FRONTEND_ORIGIN_W, FRONTEND_PORT CORS allow-list is built from the frontend origins
PostgreSQL DATABASE_URL or PGHOST/PGPORT/PGDATABASE/PGUSER/PGPASSWORD, PGSSLMODE Standard pg variables
Throttling COOLDOWN, ENABLE_RATE_LIMIT Mail cooldown + rate limiter switch
Mail OUTGOING_MAILSERVER, MAIL_SMTP_PORT, NO_REPLY_EMAIL(_PASSWORD), MAIL_NAME, … Required for login/signup/recovery mails
Crypto ENCRYPTION_KEY (32 bytes), ENCRYPTION_IV (16 bytes) AES-256-CBC for game codes / device UUIDs
Game MAXIMUM_ERROR_AMOUNT (highest error ID, 31), MAXIMUM_MISTAKE_AMOUNT Must match Unity scene content
Devices WHITELISTED_DEVICE_UUIDS _-separated; empty disables the whitelist
TLS USE_HTTPS, CERT_PATH, KEY_PATH Production only

Frontend (room-of-horror-frontend/.env.dev / .env.prod)

Validated by src/utils/Config.ts. All values are public (bundled into the browser build) — never put secrets here.

Variable Purpose
REACT_APP_USE_ENV, REACT_APP_MAINTENANCE Env check; maintenance mode switch
PORT / REACT_APP_PORT, REACT_APP_ORIGIN Dev server + own origin
REACT_APP_BACKEND_ORIGIN, REACT_APP_BACKEND_PORT, REACT_APP_BACKEND_PATH API base URL parts
REACT_APP_COOLDOWN Client-side request cooldown (keep ≥ backend COOLDOWN + 1000)
REACT_APP_SESSION_ID_INTERVAL, REACT_APP_SESSION_ID_DURATION Login polling cadence and timeout

Unity (Assets/Scripts/Env.cs)

Configured in the Unity Inspector, not via files:

Field Purpose
backendOriginValue API base URL incl. trailing / (default https://roomofhorror.de/api/; set to http://localhost:8080/api/ for local work)
useLocalTestDeviceUUID / localTestDeviceUUID / deviceUUIDOverride Device identity; without overrides a UUID is generated once and persisted in PlayerPrefs
devModeEnabled, leaveLobbyPermEnabled Debug flags / lobby-leave permission

Information sources

  • room-of-horror-api/utils/Endpoints.ts, endpoints/**, schemas/requests.ts, messages/APIMessages.ts, websocket/Events.ts, utils/Config.ts, .env.example
  • room-of-horror-frontend/.env.example, src/utils/Config.ts, src/services/*
  • room-of-horror-unity/Assets/Scripts/Env.cs, EndpointInteraction.cs
  • Root CONFIGURATION.md

Suggested improvements

  • Publish an OpenAPI specification generated from the Zod schemas (e.g. via zod-openapi) so the Postman collection and this page cannot drift from the code.

Last build: 19 Aug 2026, 05:20+00:00


This site uses Just the Docs, a documentation theme for Jekyll.