# Rikskampen X — Theme 5 API

Generated from the verified OpenAPI documents in `docs/theme5-api/`. Those documents are the contract; this collection is only a view of them, regenerated with `python3 docs/theme5-api/postman/generate_postman.py`.

## Read this first: what the API is and what is ready

- **Who it is for.** This is the **staff API** of the Theme 5 CRM: coaches and admins working on their clients. It is not an API for clients (end users) of the mobile app. How clients sign in (open decision O9) is not decided, and no client-facing endpoint exists yet. A **coach** mobile app can use these endpoints; a **client** mobile app cannot yet.
- **Contract status.** 22 operations are defined and independently reviewed:
  - 16 core operations;
  - 3 AI suggestion operations;
  - 3 sign-in endpoints.
- **Backend status.**
  - The services, the PostgreSQL schema and store, the HTTP layer (all 22 routes) and the sign-in modules and services are implemented and verified by trusted tests, on an in-process PostgreSQL and real HTTP.
  - The wiring of the sign-in into the running app is being finished now. Until then, sign-in is not yet served end to end by the app.
- **Not deployed.**
  - There is no public server or base URL yet; hosting (open decision O2) is not decided.
  - Set `baseUrl` to the server you run, for example `http://localhost:8080`.
  - Until the production database connection and hosting exist, a running server is a development setup.
- **Policy gate.** The calorie floor (D4) and the activity estimate (D5) are provisional. No real client may rely on them before a qualified nutrition professional has reviewed them.

## How to use the collection

1. Import `Theme5-API.postman_collection.json` and `Theme5-local.postman_environment.json`, then select the environment "Theme 5 — local".
2. Set `baseUrl` to your server.
3. Run **0. Staff sign-in → Sign in a coach or admin**. Its test script stores `accessToken` in the environment. Every other request sends `Authorization: Bearer {{accessToken}}` automatically.
4. The access token lasts 15 minutes. Run **Exchange the refresh cookie…** to get a new one. Postman keeps the HttpOnly refresh cookie for the server's domain, and a mobile client must keep it in its cookie store too. Do not store tokens in plain preferences.
5. Writes carry `Idempotency-Key: {{$guid}}`, a new UUID per send.

## Conventions every endpoint follows

- **JSON** in and out. Paths start with `/api/theme5`.
- **Errors** are `{"code", "message", "details"}`. `details` lists `{field, reason}` for `VALIDATION_FAILED` and `OUT_OF_BOUNDS`, and is `null` otherwise. The codes:

  | Status | Codes |
  |---|---|
  | 400 | `VALIDATION_FAILED` |
  | 401 | `UNAUTHENTICATED` (and `INVALID_CREDENTIALS` on sign-in) |
  | 403 | `FORBIDDEN` (a coach not assigned to the client) |
  | 404 | `NOT_FOUND` |
  | 409 | `STALE_REVISION`, `DAY_LOCKED`, `IDEMPOTENCY_KEY_REUSED`, `DUPLICATE_SOURCE_EVENT`, `ALREADY_DECIDED` |
  | 422 | `OUT_OF_BOUNDS`, `CONFIRMATION_REQUIRED` |
  | 429 | `TOO_MANY_ATTEMPTS` (five failed sign-ins for one email within 15 minutes) |
  | 502 | `AI_SUGGESTION_REJECTED` |
  | 503 | `AI_UNAVAILABLE` |
- **Idempotent writes.** Every write needs an `Idempotency-Key` of 8–128 characters.
  - Retrying with the same key and the same request returns the stored answer, with no second write.
  - The same key with a different request is `409 IDEMPOTENCY_KEY_REUSED`.
- **Revisions.** Writes to a day, a meal or a recipe selection send `expectedRevision`, the revision you last read. If someone changed it in between, the answer is `409 STALE_REVISION`: read again and retry.
- **Past days are read-only.** A day before today (Europe/Stockholm, weeks start on Monday) answers writes with `409 DAY_LOCKED`.
- **Identifiers and units:**
  - `clientId` matches `^[A-Za-z0-9_-]{1,64}$`;
  - `week` is 1–52, `dayIndex` is 0–6 (the day of the program week);
  - dates are `YYYY-MM-DD`;
  - energy is in kcal, distance in km;
  - an unknown value is `null`, never `0`.
- **Audit.** Every applied write is audited and returns an `audit` stamp `{auditId, actorId, at}`.
- **Authorization** is checked on the server for every request: a coach acts only for assigned clients, and an admin for every client. The role and the assignments are read on every request, so a change applies at once.
