Français
Authentification
Les routes sont relatives à <PUBLIC_BASE_URL> ; les corps de requête et de réponse sont en JSON sauf mention contraire. L’authentification, les portées, la pagination et le format des erreurs sont décrits dans les guides de l’API REST.
POST /api/v1/auth/login
Se connecter
Ouvre une session et pose le cookie de session httpOnly. Limité en débit par IP avant toute lecture en base (voir Retry-After). Un corps malformé rend le même code qu’un mot de passe faux : la route n’est pas un oracle.
Accès — Aucune authentification.
Corps de la requête (application/json)
| Champ | Type | Requis |
|---|---|---|
email | string | oui |
password | string | oui |
Schéma JSON
json
{
"type": "object",
"properties": {
"email": { "type": "string" },
"password": { "type": "string", "minLength": 1, "maxLength": 256 }
},
"required": [ "email", "password" ]
}Réponses
200 — Le membre et l’expiration de la session.
| Champ | Type | Requis |
|---|---|---|
member | object | oui |
expiresAt | string (date-time) | oui |
Schéma JSON
json
{
"type": "object",
"properties": {
"member": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"email": { "type": "string" },
"name": { "type": "string" },
"role": { "type": "string", "enum": [ "member", "admin" ] },
"status": { "type": "string", "enum": [ "active", "suspended" ] }
},
"required": [ "id", "email", "name", "role", "status" ],
"additionalProperties": false
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [ "member", "expiresAt" ],
"additionalProperties": false
}400 — La requête ne respecte pas son schéma.
Codes d’erreur — auth.invalid_credentials, auth.too_many_attempts, auth.https_required. Les codes communs (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) s’appliquent à toutes les routes ; voir Erreurs.
Exemple de requête
json
{ "email": "alice@example.test", "password": "correct horse battery staple" }Exemple de réponse (200)
json
{
"member": {
"id": "0192f1c2-0000-7000-8000-000000000001",
"email": "alice@example.test",
"name": "Alice",
"role": "admin",
"status": "active"
},
"expiresAt": "2026-10-11T09:00:00.000Z"
}POST /api/v1/auth/logout
Se déconnecter
Révoque la session et efface le cookie. Idempotent : répond 200 même sans session.
Accès — Session de membre seulement : les clés d’API sont refusées (api_key.session_required).
Réponses
200 — Déconnecté.
| Champ | Type | Requis |
|---|---|---|
status | "ok" | oui |
Schéma JSON
json
{
"type": "object",
"properties": { "status": { "type": "string", "const": "ok" } },
"required": [ "status" ],
"additionalProperties": false
}401 — Aucune session ni clé d’API valide (auth.unauthenticated).
403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).
429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.
GET /api/v1/auth/me
Qui suis-je
Le membre connecté, l’expiration de la session, les préférences publiques (langue, mode d’affichage, thème) et la date d’arrivée.
Accès — Session de membre ou clé d’API portant profile:read.
Réponses
200 — Le membre courant.
| Champ | Type | Requis |
|---|---|---|
member | object | oui |
expiresAt | string (date-time) | oui |
settings | object | oui |
joinedAt | string (date-time) | non |
Schéma JSON
json
{
"type": "object",
"properties": {
"member": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"email": { "type": "string" },
"name": { "type": "string" },
"role": { "type": "string", "enum": [ "member", "admin" ] },
"status": { "type": "string", "enum": [ "active", "suspended" ] }
},
"required": [ "id", "email", "name", "role", "status" ],
"additionalProperties": false
},
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"settings": {
"default": {},
"type": "object",
"properties": {
"locale": { "type": "string", "enum": [ "fr", "en" ] },
"theme": { "type": "string", "enum": [ "light", "dark", "system" ] },
"themeName": { "type": "string", "enum": [ "classic", "meridian", "atelier" ] }
},
"additionalProperties": false
},
"joinedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [ "member", "expiresAt", "settings" ],
"additionalProperties": false
}401 — Aucune session ni clé d’API valide (auth.unauthenticated).
403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).
429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.
POST /api/v1/auth/accept-invitation
Accepter une invitation
Crée le membre et consomme l’invitation, atomiquement. N’ouvre pas de session : le nouveau membre se connecte avec le mot de passe qu’il vient de choisir. Jeton inconnu, périmé ou consommé rendent le même code. Limité en débit comme la connexion.
Accès — Aucune authentification.
Corps de la requête (application/json)
| Champ | Type | Requis |
|---|---|---|
token | string | oui |
name | string | oui |
password | string | oui |
Schéma JSON
json
{
"type": "object",
"properties": {
"token": { "type": "string", "minLength": 1, "maxLength": 200 },
"name": { "type": "string", "minLength": 1, "maxLength": 200 },
"password": { "type": "string", "minLength": 12, "maxLength": 256 }
},
"required": [ "token", "name", "password" ]
}Réponses
201 — Le membre, créé.
| Champ | Type | Requis |
|---|---|---|
member | object | oui |
Schéma JSON
json
{
"type": "object",
"properties": {
"member": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"email": { "type": "string" },
"name": { "type": "string" },
"role": { "type": "string", "enum": [ "member", "admin" ] },
"status": { "type": "string", "enum": [ "active", "suspended" ] }
},
"required": [ "id", "email", "name", "role", "status" ],
"additionalProperties": false
}
},
"required": [ "member" ],
"additionalProperties": false
}400 — La requête ne respecte pas son schéma.
Codes d’erreur — governance.invalid_invitation, governance.member_exists, auth.too_many_attempts. Les codes communs (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) s’appliquent à toutes les routes ; voir Erreurs.
GET /api/v1/invitations/{token}
Aperçu d’une invitation
Y a-t-il un formulaire à afficher ? Rend l’adresse masquée, le nom de l’organisation et l’expiration. Contrairement à l’acceptation elle distingue ses refus : 404 inconnu, 410 périmé, 409 déjà accepté. Partage la limite de débit de l’acceptation.
Accès — Aucune authentification.
Paramètres
| Nom | Où | Type | Requis |
|---|---|---|---|
token | chemin | string | oui |
Réponses
200 — L’invitation, utilisable.
| Champ | Type | Requis |
|---|---|---|
email | string | oui |
organizationName | string | oui |
expiresAt | string (date-time) | oui |
Schéma JSON
json
{
"type": "object",
"properties": {
"email": { "type": "string" },
"organizationName": { "type": "string" },
"expiresAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [ "email", "organizationName", "expiresAt" ],
"additionalProperties": false
}Codes d’erreur — governance.invitation_invalid, governance.invitation_expired, governance.invitation_already_accepted, auth.too_many_attempts. Les codes communs (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) s’appliquent à toutes les routes ; voir Erreurs.