Skip to content

API — General conventions

This page describes the cross-cutting rules that apply to all PerfShop REST endpoints. It is the first page to read before tackling per-controller references.

Scope

The PerfShop backend exposes around 95 endpoints spread over 19 Spring controllers. All conventions listed here are applied uniformly, except where explicitly noted in the relevant controller page.


Base URL and versioning

Environment Base URL
Local development (Docker Compose) http://localhost:9080
Production (nginx on NAS) https://perfshop-api.perfshop.io

PerfShop does not use explicit versioning in the path: there is no /v1/ prefix. The entire API lives under /api/.... Changes follow a "rolling release" model — breaking changes are documented in the release notes.


Exchange format

  • Requests: JSON (Content-Type: application/json), except POST /api/admin/products/{id}/image which expects multipart/form-data.
  • Responses: JSON systematically (even for errors). Bodies are encoded in UTF-8.
  • Dates: ISO 8601 format (2026-04-08T10:30:00Z for timestamps, 1990-05-15 for bare dates).
  • Monetary amounts: BigDecimal serialized as a JSON number with 2 decimals (e.g. 49.99).
  • Identifiers: Long (64-bit integer) for DB entities, UUID (string) for tokens.

The three authentication mechanisms

PerfShop hosts three distinct authentication systems side by side, each dedicated to a user population.

flowchart LR
    subgraph CLIENT["HTTP client"]
      U1[End user<br/>browser]
      U2[Instructor<br/>chaos-admin]
      U3[Student<br/>chaos page]
    end

    subgraph API["Spring Boot backend"]
      A1[HTTP session<br/>JSESSIONID cookie]
      A2[X-Admin-Token<br/>in-memory UUID]
      A3[X-Student-Token<br/>per-student UUID]
    end

    U1 -->|"POST /api/auth/login"| A1
    U2 -->|"POST /api/admin/login"| A1
    U2 -.->|"or direct header"| A2
    U3 -->|"POST /pedagogique/join"| A3

    A1 --> B1[CartController<br/>OrderController<br/>UserController]
    A2 --> B2[AdminController<br/>ChaosController<br/>BusinessChaosController<br/>SecurityChaosController<br/>FunctionalChaosController<br/>ChaosScriptingController]
    A3 --> B3[ChaosStudentController<br/>pedagogical endpoints]

HTTP session (end user)

  • Established by POST /api/auth/login with email + password.
  • Stored server-side in the Spring HttpSession (key: LOGGED_IN_USER).
  • Automatically transmitted by the browser via the JSESSIONID cookie.
  • Duration: 30 minutes of inactivity by default (server.servlet.session.timeout).
  • Destroyed by POST /api/auth/logout (except in Business chaos level 3+ — see A11 grace period).

X-Admin-Token (instructor)

  • Generated by POST /api/admin/login in addition to the session cookie. Returned in the body under the adminToken key.
  • Random UUID stored in memory in a ConcurrentHashMap server-side (VALID_ADMIN_TOKENS key in AdminController).
  • Not persistent: reset on backend restart (intentional behavior — pedagogical value for memory chaos demos).
  • Designed for cross-origin use (curl scripts, automated tests, calls from chaos-admin which is served by nginx on another port).
  • Transmitted in the HTTP header: X-Admin-Token: 550e8400-e29b-41d4-a716-446655440000.

Dual authentication

Admin controllers accept either the session cookie OR the X-Admin-Token header. The AdminAuth.isAdmin(session, token) utility centralizes this check.

X-Student-Token (student)

  • Generated by POST /api/chaos/student/pedagogique/join when a student joins a journey.
  • UUID unique per student, stored server-side in PedagogiqueSessionService (database + optional memory cache).
  • Used by pedagogical endpoints (/validate, /logique/questions, /finale/validate, etc.) to isolate each student's progression.
  • Transmitted in the HTTP header: X-Student-Token: ab12cd34-....
  • Also allows orders (POST /api/orders) to receive the pedagogical agentCode if the student is on an active journey.

Standard error format

Any error response (4xx or 5xx) returns a JSON object containing at least the error key:

{ "error": "Not authenticated" }

Some errors add contextual keys:

{
  "error": "LICENSE_REQUIRED",
  "chaos": "performance",
  "requested": 3,
  "maxFree": 1,
  "message": "Level 3 not available without license. Maximum: 1",
  "portalUrl": "https://perfshop.io"
}
{
  "error": "Invalid data",
  "fields": {
    "phone": "Invalid phone format for FR — expected: 10 digits starting with 0 (e.g. 0612345678)",
    "postalCode": "Invalid FR postal code '7500' — expected: 5 digits (e.g. 75001)"
  }
}

Messages are always resolved server-side via I18nService from messages_XX.properties keys — never returned as raw keys.


HTTP codes used

PerfShop strictly follows standard HTTP semantics, with an important particularity for code 402.

Code Usage in PerfShop
200 OK Standard success of a GET request or an idempotent action
201 Created Resource creation (POST /api/orders, POST /api/admin/products, POST /api/admin/users)
400 Bad Request Missing or invalid parameter, incorrect JSON format
401 Unauthorized Missing or invalid authentication (no session, unknown token)
402 Payment Required Feature reserved for paid licenses (freemium). See freemium vs Pro
403 Forbidden Authenticated but not authorized (student mode locked, non-superadmin account, resource from another user)
404 Not Found Resource not found — also returned by the hidden admin portal when S10–S12 are inactive (concealment)
409 Conflict State conflict: incorrect pedagogical step, license already active, no active journey
422 Unprocessable Entity Business validation failed (email format, Luhn card, birth date, etc.)
429 Too Many Requests Limit reached: /pedagogique/join refuses beyond 500 simultaneous sessions
500 Internal Server Error Unhandled exception, DB deadlock, functional chaos error (NPE, StackOverflow, OOM)

The special case of 402 Payment Required

PerfShop uses the 402 Payment Required code systematically to signal that a feature exceeds the current license limits. This is a deliberate API choice: it makes the freemium rule self-describing — an HTTP client can unambiguously detect that it is hitting a license wall, separately from a plain 403 Forbidden.

Endpoints that return 402 all carry the same LICENSE_REQUIRED body structure documented above.


Internationalization of error messages

PerfShop is multilingual, but does not negotiate language via HTTP Accept-Language. The language is fixed globally by an environment variable read at backend startup:

PERFSHOP_LANG=fr   # default
PERFSHOP_LANG=en
PERFSHOP_LANG=es   # (upcoming)
PERFSHOP_LANG=de   # (upcoming)
PERFSHOP_LANG=it   # (upcoming)

Practical consequences:

  • A given deployment speaks a single language.
  • To change language, the backend must be restarted with another value of PERFSHOP_LANG.
  • Messages are resolved from messages_fr.properties or messages_en.properties (or future files).
  • Accept-Language headers sent by the client are ignored.

This choice is driven by the pedagogical use case: a training room works in a given language, and the consistency of logs and error messages across all workstations matters more than per-user localization.

See Internationalization for the mechanism details.


CORS headers

The backend accepts cross-origin calls from internal frontends via CorsConfig.java. Allowed origins in development typically include:

  • http://localhost:3010 (React e-commerce frontend)
  • http://localhost:3011 (welcome home page)
  • http://localhost:3012 (chaos-admin)
  • http://localhost:3013 (monitoring)
  • http://localhost:3014 (scripts-ui, jmeter-ui)

The X-Admin-Token and X-Student-Token headers are explicitly exposed in the CORS configuration to allow reading/writing from browsers.

In production, origins are restricted to *.perfshop.io domains.


Pagination

Endpoints returning paginated lists use Spring Data Pageable. Standard format:

Request:

GET /api/products?page=0&size=20&sort=price

Response:

{
  "content": [ /* ... page elements ... */ ],
  "totalElements": 147,
  "totalPages": 8,
  "number": 0,
  "size": 20,
  "first": true,
  "last": false
}
Parameter Default Description
page 0 Page number (0-indexed)
size 20 Number of elements per page
sort varies Sort column (server-side whitelist to avoid PropertyReferenceException)

Sort whitelist

For GET /api/products, only the columns id, name, price, category, createdAt, stock are accepted. An unknown sort parameter is silently replaced by id.


Specific response headers

Header Emitted by Description
X-Debug-Token POST /api/auth/login Only in Security chaos level ≥ 3 (fault S7). Weakly signed HMAC token.
X-Session-Token Checkout endpoints Scripting chaos level ≥ 1 — see Scripting chaos
X-Request-ID Checkout endpoints Scripting chaos level ≥ 1
X-Action-Token Checkout endpoints Scripting chaos level ≥ 2
X-CSRF-Token Checkout endpoints Scripting chaos level ≥ 3
X-Step-Token Checkout endpoints Scripting chaos level ≥ 3
X-Signature Checkout endpoints Scripting chaos level ≥ 3 (HMAC)

These headers are documented in detail on the Scripting chaos page.


Endpoint naming conventions

PerfShop follows a tree organized by business function then by role:

/api/auth/*              → end-user authentication
/api/products/*          → product catalog (public)
/api/cart/*              → cart (public or logged in)
/api/checkout/*          → 4-step checkout journey
/api/orders/*            → orders (logged in)
/api/countries           → reference lists

/api/admin/*             → administration (instructor)
/api/admin/chaos/*       → chaos control (instructor)
/api/admin/portal/*      → hidden portal (Security chaos Master level)

/api/chaos/public/*      → chaos reads (monitoring, no auth)
/api/chaos/frontend/*    → frontend chaos state relay
/api/chaos/student/*     → student page (pedagogical journeys)

/api/license/*           → license management

The /api/chaos/public/* and /api/chaos/frontend/state (on GET) endpoints are excluded from the ChaosInterceptor, which guarantees that they always respond — even when the rest of the backend is saturated by active chaos. This is the property that lets student monitoring keep displaying a consistent state under simulated failure conditions.


Minimal curl example

Scenario: user login, profile retrieval, adding a product to the cart.

# 1. Login — stores the session cookie in /tmp/cookies.txt
curl -c /tmp/cookies.txt \
     -H "Content-Type: application/json" \
     -d '{"email":"alice@example.com","password":"alice123"}' \
     http://localhost:9080/api/auth/login

# 2. Profile (reuses the cookie)
curl -b /tmp/cookies.txt http://localhost:9080/api/auth/me

# 3. Add to cart
curl -b /tmp/cookies.txt \
     -H "Content-Type: application/json" \
     -d '{"productId":1,"quantity":2}' \
     http://localhost:9080/api/cart/add

Admin scenario with X-Admin-Token (no cookie):

# 1. Admin login — retrieves adminToken from the response JSON
TOKEN=$(curl -s \
    -H "Content-Type: application/json" \
    -d '{"email":"admin@perfshop.fr","password":"admin"}' \
    http://localhost:9080/api/admin/login | jq -r '.adminToken')

# 2. Drive CPU chaos to level 3
curl -H "X-Admin-Token: $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"intensity":80,"ratio":2}' \
     http://localhost:9080/api/admin/chaos/cpu

# 3. Consolidated status
curl -H "X-Admin-Token: $TOKEN" \
     http://localhost:9080/api/admin/chaos/status

Cross-cutting points of attention

Intentionally vulnerable endpoints

Some endpoints become deliberately vulnerable when Security chaos exceeds a certain level. These behaviors are pedagogical: they serve to illustrate OWASP faults in a controlled environment. The Security chaos page lists these faults exhaustively, and each endpoint page signals when it is affected.

The hidden admin portal

The AdminPortalController (/api/admin/portal/*) systematically returns 404 when Security chaos is strictly below level 4 (Master). This behavior is intentional — it lets the student discover the portal by fuzzing only when the instructor has enabled the Master level. See admin-portal.md.

Freemium 402 never blocks the backend

An endpoint that returns 402 does not interrupt chaos already running: it only prevents a student without a license from activating new ones beyond the allowed free level. The instructor with a license can keep driving chaos normally.