Contrat API — Activation d’une licence par le client
Destinataire : développeur de l’application mobile
Base : https://live.jmoai.net · Authentification : Authorization: Bearer <jwt client>
Dernière mise à jour : 2026-09-13
Ce contrat décrit ce que le serveur garantit au moment où un client active une licence, et ce qu’il ne garantit pas. Il existe parce qu’un client a vécu l’inverse : l’application annonçait « activé avec succès » et rien n’arrivait sur son compte.
1. POST /v1/licenses/activate
Active une licence par sa CLÉ (voucher JPS-…, clé de licence JSVC-…, clé Chario).
// Corps
{ "code": "JSVC-MIKHMOAIPRO-02386-06BF0" }
// 200 — première activation, ou activation d'une licence encore réclamable
{
"success": true,
"already_active": false,
"message": "Licence activée avec succès !",
"next_step": "Provisionnez votre routeur depuis l’application pour ouvrir l’accès.",
"data": { "provider": "local", "plan_code": "MIKHMOAI_PRO", "expires_at": "2027-09-12T…" },
}
// 200 — la licence était DÉJÀ portée par ce compte (voir §3)
{
"success": true,
"already_active": true,
"message": "Cette licence est déjà active sur votre compte — services vérifiés.",
"next_step": "Provisionnez votre routeur depuis l’application pour ouvrir l’accès.",
"data": { "…": "…" },
}
Ce que le serveur fait, à chaque appel — y compris sur une licence déjà active
- Droits : la carte du plan est redérivée du catalogue et écrite sur la licence
(
license_entitlements). Aucun droit n’est recopié à la main. - Boutique : créée si le plan l’accorde (
web_store_accessouvoucher_sales), et rattachée à cette licence-là. - RADIUS : accès créé si le plan l’accorde (
radius_hotspot_management). Un accès déjà vivant n’est pas reprovisionné — la création prolonge l’échéance. - VPN : emplacement attribué si le plan accorde
vpn_remote_management/vpn_slots. - État du compte : plan et échéance du compte suivent la meilleure licence active.
L’appel est idempotent : le rejouer ne duplique rien et ne prolonge rien.
Ce que le serveur NE garantit PAS
- Un service visible immédiatement. L’activation ouvre un DROIT ; le tunnel, lui, existe
quand le routeur est provisionné depuis l’application, qui reste la source de vérité.
C’est le sens de
next_step: sans cette phrase, le client attend un accès qui ne viendra pas tout seul. - Un service EN PLUS pour chaque licence. Si le compte est déjà couvert (un emplacement actif protégé par une autre licence active), activer une licence de plus ne crée pas forcément un second emplacement. Le serveur ne coupe rien dans ce cas : il ne double pas.
2. POST /v1/licenses/claim et /claim-guest
Mêmes garanties que §1. Différences : la licence passe de PENDING_DISTRIBUTION à ACTIVE,
le titulaire devient le compte réclamant, et claim-guest rend une session (Set-Cookie) pour
auto-connexion.
// 200
{
"success": true,
"message": "Licence activée et rattachée à votre compte.",
"already_claimed": false,
"data": { "id": "LOT-822DD9_001", "plan_code": "MIKHMOAI_PRO", "expires_at": "…" },
}
Nouveau champ (2026-09-13) sur les deux routes, renseigné quand already_claimed / already_active vaut true :
"services": {
"entitlements": "A_JOUR", // ou "NON_FAIT"
"boutique": "LIVREE", // "DEJA_ACTIVE" | "NON_CONCERNEE" | "ECHEC" | null
"radius": "DEJA_ACTIF", // "LIVRE" | "NON_CONCERNEE" | "ECHEC" | null
"vpn": "VERIFIE", // "NON_CONCERNEE" | "ECHEC" | null
"etat_commercial": "A_JOUR",
"echecs": [] // noms des services non délivrés, à signaler au support
}
services est informative : l’application peut l’afficher en cas d’échec (« certains
services n’ont pas pu être vérifiés, notre équipe est prévenue »). Elle ne doit pas en déduire
un état local : les droits se lisent toujours côté serveur.
3. Ce qui n’est PAS une erreur
| Situation | Réponse | Pourquoi ce n’est pas une erreur |
|---|---|---|
| La licence est déjà active sur ce compte | 200, already_active: true |
Rien à réactiver. Le serveur revérifie et re-livre ce qui manque, puis le dit. |
| La licence active appartient à un autre compte | 400, code ALREADY_CLAIMED_BY_OTHER |
Refus définitif, avec le motif. La clé ne doit pas circuler. |
success: true mais aucun service nouveau visible |
— | Le compte était déjà couvert. Aucun écran d’erreur ne doit s’afficher. |
| Le plan n’accorde pas la boutique ou RADIUS | — | Ce n’est pas un échec : le service n’était pas vendu. |
4. Déviation assumée — révocation d’un droit
Le serveur résout les droits d’un client par UNION des licences ACTIVES non expirées : un
droit accordé par une licence ne peut pas être annulé par le enabled: false d’une AUTRE
licence du même compte. Avant le 2026-09-13, l’inverse était vrai et 84 clients perdaient des
droits payés (458 couples client×droit), dans un ordre qui dépendait de la lecture en base.
Pour révoquer un droit à un client, on révoque ou on expire la licence qui l’accorde
(licenses.status), on n’écrit pas un drapeau sur une licence voisine. Cette section existe
pour que personne ne « rétablisse » l’ancien comportement en croyant corriger un oubli.