API — Introduction & authentification
Deux surfaces API
Section intitulée « Deux surfaces API »| Surface | Base URL | Auth | Usage |
|---|---|---|---|
| API v1 | /v1/... | JWT Bearer + MFA, API-key, OAuth2 | Fronts tenant/reception/mobile, intégrateurs |
| API admin | /admin/... | Cookie session cp_session ou header x-platform-admin-key | Console Logmis control-plane uniquement |
Les specs OpenAPI sont disponibles dans le monorepo :
apps/api/openapi.json— 128 paths, 141 opérations (API v1)apps/api/openapi.admin.json— 9 paths (API admin)
Authentification API v1
Section intitulée « Authentification API v1 »1. JWT + MFA (comptes utilisateurs)
Section intitulée « 1. JWT + MFA (comptes utilisateurs) »Le flux standard pour les fronts (ADR-00D85 — résolution tenant globale) :
# Étape 1 — Login : email + mot de passe uniquement, sans x-tenant-idPOST /sessionsContent-Type: application/json
{ "email": "...", "password": "..." }
# Réponse si MFA requis :{ "mfaRequired": true, "sessionToken": "<opaque>" }
# Étape 2 — Vérification TOTPPOST /sessions/mfa/verify{ "sessionToken": "<opaque>", "code": "123456" }
# Réponse :{ "accessToken": "eyJ...", "refreshToken": "<opaque>" }
# Étape 3 — Récupérer le contexte utilisateur (tenantId inclus)GET /v1/meAuthorization: Bearer <accessToken>
# Réponse :{ "data": { "userId": "...", "tenantId": "...", "sessionId": "...", "roles": [...], "establishments": [...] } }Important — résolution tenant (ADR-00D85) : le login (POST /sessions) et les endpoints d’enrôlement MFA n’exigent pas de header x-tenant-id. Le serveur résout le tenant depuis l’email, qui est unique globalement. Le header x-tenant-id est requis uniquement sur POST /sessions/refresh — le front doit persister le tenantId retourné par GET /v1/me (champ data.tenantId) après le premier login.
Le accessToken est un JWT HS256 signé avec JWT_SECRET. Il contient :
sub— userId (UUID)tid— tenantId (UUID)scope— scope RBAC (OrgPath)eid— establishmentId (résiduel : absent sur mobile-staff)- Durée de vie : 15 minutes
Le refreshToken est opaque, haché en DB (reuse-detection activée).
2. Enrôlement MFA (premier login)
Section intitulée « 2. Enrôlement MFA (premier login) »Si le compte n’a pas encore confirmé son TOTP, le login retourne MFA_ENROLLMENT_REQUIRED :
# Démarrer l'enrôlement (x-tenant-id optionnel — résolution depuis l'email)POST /sessions/mfa/enroll/begin{ "email": "...", "password": "..." }
# Réponse : URI TOTP + secret base32 à scanner ou saisir manuellement{ "otpauthUri": "otpauth://totp/...", "secret": "<base32>" }
# Confirmer avec le premier code valide — ouvre la sessionPOST /sessions/mfa/enroll/confirm{ "email": "...", "password": "...", "code": "123456" }
# Réponse : accessToken + refreshToken (session ouverte){ "accessToken": "eyJ...", "refreshToken": "<opaque>", "expiresAt": "..." }Le secret TOTP n’est jamais persisté côté front — il est généré et chiffré AES-256-GCM en DB uniquement après confirmation du premier code valide. Il n’est exposé qu’une seule fois, à enroll/begin.
3. API-key (intégrateurs)
Section intitulée « 3. API-key (intégrateurs) »Format : pmsk_live_<base62> (préfixe pmsk_live_ vérifié côté serveur).
GET /v1/reservationsAuthorization: pmsk_live_<key>Les API-keys passent par le RBAC chemin unique (même PermissionResolver que les JWT). Rate-limit par tier (X-RateLimit-* headers).
4. OAuth2 client-credentials (machine-to-machine)
Section intitulée « 4. OAuth2 client-credentials (machine-to-machine) »POST /oauth/tokenContent-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=<id>&client_secret=<secret>&scope=<scope>
# Réponse :{ "access_token": "eyJ...", "token_type": "bearer", "expires_in": 3600 }L’access_token OAuth2 est un JWT — transporté en Authorization: Bearer sur toutes les requêtes suivantes. Le subject est synthétique (pas un userId humain).
5. SSO OIDC
Section intitulée « 5. SSO OIDC »Le flux OIDC (Authorization Code avec PKCE) permet l’account-linking. Endpoints : GET /v1/sso/authorize, GET /v1/sso/callback. JIT provisioning opt-in. Deny par défaut (aucun provider SSO = pas d’accès SSO).
Authentification API admin
Section intitulée « Authentification API admin »Deux modes :
Session web (console admin) :
POST /admin/cp-sessions{ "email": "admin@platform.internal", "password": "...", "totpCode": "123456" }# → cookie httpOnly Secure SameSite=Strict cp_session# Les requêtes suivantes transportent le cookie automatiquementClé API CLI/machine :
GET /admin/tenantsx-platform-admin-key: <clé opérateur>La session web expire après 15 min d’inactivité ou 4h absolues. CSRF double-submit Origin fail-closed.
Enveloppe de réponse
Section intitulée « Enveloppe de réponse »Toutes les réponses succès suivent la même structure :
// Ressource unique{ "data": { ... }}
// Liste paginée{ "data": [ ... ], "page": 1, "pageSize": 20, "total": 142}{ "code": "RESERVATION_NOT_FOUND", "message": "Réservation introuvable", "origin": { "boundedContext": "reservation", "module": "reservation" }, "status": 404, "traceId": "01HXYZ..."}| Code HTTP | Signification |
|---|---|
| 400 | Validation Zod échouée (body/query malformés) |
| 401 | Token absent, expiré ou révoqué |
| 403 | Permission insuffisante (RBAC) ou IDOR (scope OrgPath) |
| 404 | Ressource inexistante ou hors du scope tenant |
| 409 | Conflit métier (ex: UNIT_TYPE_ALREADY_EXISTS) |
| 422 | Erreur domaine (ex: RESERVATION_ALREADY_CONFIRMED) |
| 429 | Rate limit dépassé |
| 500 | Erreur interne (incluant le traceId pour le support) |
Les codes métier (code) sont ceux de DomainError (ex: INVENTORY_INSUFFICIENT, FOLIO_CLOSED).
Idempotency-Key
Section intitulée « Idempotency-Key »Pour les opérations non-idempotentes (POST), le header Idempotency-Key permet la déduplication :
POST /v1/reservationsIdempotency-Key: my-unique-key-2026-abc123Si le même appel est rejoué avec la même clé, le serveur retourne la réponse originale sans créer de doublon. La clé est stockée dans Redis avec un TTL de 24h.
Contraintes : 8 à 200 caractères, opaque (pas de sémantique imposée côté client).
Pagination
Section intitulée « Pagination »Les listes sont paginées par offset :
GET /v1/reservations?page=2&pageSize=50| Paramètre | Défaut | Maximum |
|---|---|---|
page | 1 | - |
pageSize | 20 | 100 |
Sécurité IDOR
Section intitulée « Sécurité IDOR »Chaque requête est vérifiée en 3 couches :
- RLS Postgres — filtre automatique par
tenant_id ScopeAuthorizer(couche HTTP) — vérifie que la ressource appartient à l’établissement/mandat du JWTsubjectdu JWT — jamais extrait du body, toujours du token
Un utilisateur ne peut accéder qu’aux ressources dans son home_scope OrgPath (ADR-00D82).
Tags API v1 (référence)
Section intitulée « Tags API v1 (référence) »Les opérations sont groupées par tag dans la référence :
reservations · folios · deposits · guests · travelers · housekeeping · work-orders · inventory · out-of-service · rate-plans · allotments · availability · rack · reporting · organization · identity · users · long-stay-contracts · mice-events · mice-equipment · main-courante · inspections · meters · cashless · owner-revenue · storage