Contrat API — Interpréter nos réponses (app mobile)
Le principe en une phrase : une requête qui n’aboutit pas côté app n’est pas une opération qui a échoué côté serveur. Avant d’afficher « échec », relisez l’état.
Ce guide s’adresse au développeur de l’application mobile. Il corrige des faux échecs
observés en production le 2026-08-16 sur MikhmonPro/4.5.1.
1. Le cas qui a motivé ce guide
Un enrôlement RADIUS a été signalé « échec » à l’utilisateur. Voici ce qui s’est réellement passé, à la seconde près :
18:59:10 POST /api/v1/radius/nas requête reçue par le backend
18:59:10 FreeRADIUS redémarre le NAS est écrit, le service recharge sa conf
18:59:18 FreeRADIUS est reparti 8 secondes plus tard
journal HTTP : statut « - » l'app avait déjà fermé la connexion
L’opération a réussi. Le NAS était bien enregistré, actif, rattaché au bon routeur. L’utilisateur, lui, a vu un échec — et a recommencé, déclenchant un nouveau redémarrage.
Un statut vide dans nos journaux ne signifie pas « erreur » : il signifie que personne n’attendait plus la réponse.
Ce n’est pas un cas isolé
Sur les 14 890 requêtes émises par l’application le 2026-08-16 :
| Réponse | Volume | Lecture |
|---|---|---|
304 |
6 856 | données inchangées — pas une erreur |
200 |
5 996 | succès |
| abandonnée | 1 112 | l’app a raccroché avant la réponse |
403 |
617 | droit absent — inutile de réessayer |
401 |
161 | jeton à rafraîchir |
409 |
21 | conflit — souvent déjà fait |
429 |
11 | quota atteint |
500 |
5 | vraie erreur serveur |
1 112 requêtes abandonnées, soit une sur treize. Chacune peut correspondre à une opération qui a abouti côté serveur pendant que l’écran affichait un échec.
2. Les délais d’attente à prévoir
Toutes les écritures ne se valent pas. Certaines déclenchent un travail d’infrastructure :
| Appel | Attendre au moins | Pourquoi |
|---|---|---|
POST /api/v1/radius/nas |
30 s | écrit le NAS puis recharge FreeRADIUS |
POST /api/v1/clients/vpn/provision |
30 s | pose le pair WireGuard et publie les ports |
Lectures (GET) |
10 s | agrégations côté serveur |
Un délai plus court ne protège pas l’utilisateur : il lui cache seulement le résultat.
3. La règle d’or : relire avant de déclarer un échec
Sur timeout ou connexion coupée, ne concluez jamais à l’échec. Relisez l’état :
POST /api/v1/radius/nas → timeout
↓
GET /api/v1/radius/nas → le NAS y est ? → SUCCÈS (afficher « déjà configuré »)
→ absent ? → proposer de réessayer
Le même raisonnement vaut pour le provisioning VPN et la création de profils. Une écriture sans réponse est un état inconnu, pas un état échoué.
4. Les réponses à ne pas traiter comme des erreurs
409 — conflit, souvent « c’est déjà fait »
L’enrôlement d’un NAS est idempotent : si le routeur est déjà rattaché à la même
allocation, le backend renvoie l’enregistrement existant avec idempotent: true dans le
corps. C’est un succès, à présenter comme « déjà configuré ».
Un vrai conflit porte un code explicite dans le corps :
code |
Sens | Action utilisateur |
|---|---|---|
RADIUS_NASNAME_CONFLICT |
l’adresse de tunnel est prise par un autre NAS | contacter le support |
RADIUS_ALLOCATION_CONFLICT |
l’allocation est rattachée à un NAS incohérent | contacter le support |
VPN_TUNNEL_NOT_CONNECTED |
le tunnel doit être monté d’abord | reconnecter, puis réessayer |
VPN_ALLOCATION_INACTIVE |
le VPN n’est pas actif | vérifier l’abonnement |
ROUTER_SERIAL_MISMATCH |
le routeur n’est pas celui verrouillé | vérifier l’équipement |
Affichez le message du corps, pas « erreur 409 ». Chacun de ces cas appelle une action différente de l’utilisateur.
202 — accepté, pas terminé
POST /api/v1/clients/remote-access/provisioning-events répond 202. L’événement est pris
en compte, le traitement continue en arrière-plan. Ce n’est ni un succès final ni un échec :
n’affichez pas de résultat, laissez l’écran en cours.
304 — rien n’a changé
46 % de votre trafic. Réutilisez les données en cache : ne videz pas l’écran, ne montrez pas d’erreur, ne relancez pas la requête.
403 — le droit manque, pas la connexion
617 réponses 403 aujourd’hui, surtout sur /radius/analytics, /radius/sessions et
/radius/vouchers. Un 403 ne se répare pas en réessayant : la licence ou l’entitlement
n’autorise pas cet appel. Cessez d’interroger l’endpoint pour cette session et affichez
l’écran « fonctionnalité non incluse ».
Réessayer en boucle consomme le quota partagé et fait échouer d’autres appels légitimes.
5. Quota — 100 requêtes / 15 min par IP, partagées par TOUTES les routes /api
11 réponses 429 aujourd’hui. Le piège est connu : une vue qui sonde en boucle consomme le
quota, et ce sont les autres appels — licence, VPN, paiement — qui échouent. La route
fautive, elle, semble fonctionner.
À corriger en priorité, d’après le trafic observé :
GET /clients/dashboard— 3 052 appels en une journée ;POST /clients/auth/push-token— 891 appels : le jeton ne change pas à chaque écran ;GET /radius/analytics/sales/history?limit=5000— appelé en rafale, parfois deux fois dans la même seconde depuis deux adresses.
Règles : interroger à l’ouverture de l’écran, rafraîchir sur action explicite, une
seule sonde à la fois (annuler la précédente), et respecter les en-têtes de cache — le 304
prouve qu’ils fonctionnent déjà.
6. Ce qu’il faut afficher à l’utilisateur
| Situation | À afficher |
|---|---|
| timeout, puis relecture confirmant l’état | « Configuration terminée » |
| timeout, puis relecture ne confirmant rien | « Vérification impossible, réessayez » |
409 avec idempotent: true |
« Déjà configuré » |
409 métier |
le message du corps, tel quel |
403 |
« Fonctionnalité non incluse dans votre licence » |
401 |
rafraîchir le jeton, sans message d’erreur |
429 |
« Trop de requêtes, patientez » — et arrêter les sondes |
500 |
« Erreur serveur » — le seul cas où le mot « erreur » est juste |
Ne montrez jamais un code HTTP brut. Et surtout : ne dites pas « échec » quand vous ne savez pas. Un utilisateur qui croit avoir échoué recommence, et chaque reprise d’un enrôlement RADIUS redémarre FreeRADIUS pour tout le parc.
7. Voir aussi
- Provisioning routeur :
https://live.jmoai.net/docs/CONTRAT-API-PROVISIONING-ROUTEUR-MOBILE - Ports et accès distant :
https://live.jmoai.net/docs/CONTRAT-API-ACCES-DISTANT-PORTS - État de la boutique :
https://live.jmoai.net/docs/CONTRAT-API-BOUTIQUE-TIKETMOMO - Référence complète :
https://live.jmoai.net/api-docs - Source Markdown :
https://live.jmoai.net/docs/CONTRAT-API-REPONSES-APP-MOBILE/raw