Comptes
POST /auth/otp/request — demander un code
Section intitulée « POST /auth/otp/request — demander un code »{ "identifier": "salma@example.com" }Réponse 202 :
{ "message": "Un code vient d’être envoyé à votre adresse e-mail.", "identifier": "salma@example.com", "channel": "mail", "expires_at": "2026-09-14T10:05:00+01:00" }Refus 422 — identifiant invalide ; compte non invité pendant la phase privée ; compte désactivé ; téléphone tant que le canal SMS/WhatsApp n’est pas configuré. Refus 429 — trop de demandes.
POST /auth/otp/verify — échanger le code contre un jeton
Section intitulée « POST /auth/otp/verify — échanger le code contre un jeton »{ "identifier": "salma@example.com", "code": "482913", "device": { "key": "a3f1…", "platform": "android", "name": "Pixel de Salma", "app_version": "0.3.0" }}device.key est un identifiant stable généré par l’application et conservé sur l’appareil ; platform vaut ios, android ou web.
Réponse 201 :
{ "token": "12|kD9…", "token_type": "Bearer", "expires_at": null, "user": { "id": 3, "name": "سلمى التازي", "email": "salma@example.com", "phone": null, "role": "learner", "status": "active", "locale": "ar", "verified_at": "…" }, "device": { "id": 5, "device_key": "a3f1…", "platform": "android", "name": "Pixel de Salma", "app_version": "0.3.0", "last_seen_at": "…" }}Refus 422 — code faux (le message indique les tentatives restantes), expiré, déjà utilisé, ou invalidé par une nouvelle demande. Se reconnecter depuis le même device.key remplace le jeton précédent.
GET /me — le compte connecté
Section intitulée « GET /me — le compte connecté »Avec Authorization: Bearer <jeton>. Rend user et la liste devices du compte. Sans jeton valide : 401.
GET /me/library — ma bibliothèque
Section intitulée « GET /me/library — ma bibliothèque »Les produits sur lesquels le compte a un droit actif, directement ou par une offre qui les contient : le parcours donne ses cinq programmes. Chaque entrée est un produit de liste, avec en plus via (le produit dont le droit découle), granted_at et expires_at. Les extraits gratuits n’y figurent pas : ils s’écoutent sans droit.
Avec un jeton, chaque produit du catalogue porte aussi owned : vrai s’il est dans la bibliothèque. Sans jeton, owned vaut null.
POST /auth/logout — se déconnecter
Section intitulée « POST /auth/logout — se déconnecter »Révoque le jeton de cet appareil. Les autres appareils restent connectés.
Enchaînement recommandé côté application
Section intitulée « Enchaînement recommandé côté application »- Écran d’identifiant →
otp/request. Afficherexpires_atet permettre de redemander après le délai. - Écran de code →
otp/verifyavec la description de l’appareil. Stocker le jeton dans le coffre sécurisé de la plateforme. - Envoyer
Accept-Language: aret le jeton sur chaque appel. Sur un 401, revenir à l’écran d’identifiant.