Read this when changing the JSON API contract, request authorization, or a React call to `/api/v1`. # API Routes All routes are registered in `src/Core/Api/Api.php`. Responses are JSON with `Cache-Control: no-store`. `CSRF` means `X-CSRF-Token` must equal the token stored in the PHP session. | Method | Path | Handler | Description | Authorization | |---|---|---|---|---| | GET | `/api/v1/auth/session` | `AuthController::session` | Returns authenticated state, safe user DTO with numeric role IDs, and CSRF token. | Public | | POST | `/api/v1/auth/login` | `AuthController::login` | Validates JSON credentials, regenerates the session ID, stores the user, and returns session state. | Public | | POST | `/api/v1/auth/logout` | `AuthController::logout` | Clears session and returns anonymous state. | CSRF | | GET | `/api/v1/navigation` | `NavigationController::index` | Returns permission-filtered, ordered menu entries. | Authenticated | | GET | `/api/v1/admin/config` | `AdminController::load` | Returns decrypted configuration text. | Role ID `1` | | PUT | `/api/v1/admin/config` | `AdminController::save` | Encrypts and replaces configuration text. | Role ID `1` + CSRF | | GET | `/api/v1/admin/users` | `UserController::index` | Returns safe user DTOs with numeric role IDs and persistent role choices. | Role ID `1` | | POST | `/api/v1/admin/users` | `UserController::create` | Creates a user from username, password, and existing role IDs. | Role ID `1` + CSRF | | PUT | `/api/v1/admin/users/@id` | `UserController::update` | Updates selected user fields and existing role IDs. | Role ID `1` + CSRF | | DELETE | `/api/v1/admin/users/@id` | `UserController::delete` | Deletes an eligible user. | Role ID `1` + CSRF | | GET | `/api/v1/admin/roles` | `RoleController::list` | Lists the persistent role catalog as safe `{id, name, description}` DTOs. | Role ID `1` | | POST | `/api/v1/admin/roles` | `RoleController::create` | Creates a role catalog record. | Role ID `1` + CSRF | | PUT | `/api/v1/admin/roles/@id` | `RoleController::update` | Updates role metadata without changing its ID. | Role ID `1` + CSRF | | DELETE | `/api/v1/admin/roles/@id` | `RoleController::delete` | Deletes a role only when it is not ID `1` and is not assigned to any user. | Role ID `1` + CSRF | | GET | `/api/v1/inventory/items` | `InventoryController::index` | Lists Inventory Monitor demo item DTOs. | Role ID `2` | | POST | `/api/v1/inventory/items` | `InventoryController::create` | Creates an Inventory Monitor demo item. | Role ID `2` + CSRF | | PUT | `/api/v1/inventory/items/@id` | `InventoryController::update` | Updates an Inventory Monitor demo item. | Role ID `2` + CSRF | | DELETE | `/api/v1/inventory/items/@id` | `InventoryController::delete` | Deletes an Inventory Monitor demo item. | Role ID `2` + CSRF | | GET | `/api/v1/notifications` | `NotificationController::index` | Returns notifications grouped by caption. | Authenticated | | DELETE | `/api/v1/notifications` | `NotificationController::delete` | Clears every notification. | Authenticated + CSRF | | DELETE | `/api/v1/notifications/@code` | `NotificationController::delete` | Deletes notifications matching a non-negative integer code. | Authenticated + CSRF | ## Contract Notes - `Controller::decodeJsonRequest()` accepts only a JSON object; malformed or empty bodies are rejected. - The authentication/session response is the source of the current CSRF token. The client caches it only in memory. - Unauthorized routes return 401; insufficient roles return 403; role, user, and inventory business conflicts return 409 with a stable `code`. - Role authorization uses persistent numeric IDs: ID `1` is Administrator and ID `2` is Inventory operator. Renaming a role does not change access. - Role deletion rejects the protected Administrator role (ID `1`) and any role assigned to a user; these business conflicts return 409. - Inventory routes require role ID `2`; ID `1` does not implicitly grant inventory access. State-changing role, user, and inventory requests require the session CSRF token. - Route registration is separate from legacy system methods that are not registered in `System::setSystemRoutes()`. See [identity/spec.md](identity/spec.md), [configuration/spec.md](configuration/spec.md), and [rules/add-api-endpoint.md](rules/add-api-endpoint.md).