Contrat API — Restitution d’une licence au client final
Destinataire : développeur de l’application mobile
Base : https://live.jmoai.net · Enveloppe : { success: boolean, data: …, error?: { code, message } }
Authentification : Authorization: Bearer <jwt client>
Le parcours
Le technicien achète la licence et la rattache à son compte pour configurer et provisionner l’équipement — sans titulaire, rien ne se provisionne. Quand le client vient chercher son matériel, le technicien rend la licence, et le client la réclame.
technicien : POST /v1/licenses/:id/release → la licence devient réclamable
client : POST /v1/licenses/claim (ou claim-guest) → il en devient titulaire
⚠️ La restitution ne coupe rien. Le matériel remis continue de fonctionner entre la remise et le geste du client — qui peut avoir lieu le lendemain. La bascule du tunnel, du port d’accès distant et du NAS se fait au moment de la réclamation, sans interruption.
1. POST /v1/licenses/:id/release
Rend la licence. Réservé à son titulaire actuel.
// Corps — les deux champs sont facultatifs
{
"recipient_email": "client@exemple.com", // recommandé, voir ci-dessous
"reason": "Remise du matériel au client",
}
// 200
{
"success": true,
"data": {
"license_id": "lic_…",
"license_key": "MK-…",
"released_to_email": "client@exemple.com",
"claimable_until": "2026-11-14T…Z",
"transfers_used": 0,
"transfers_max": 1,
},
}
Désignez recipient_email dès que vous le connaissez. Sans lui, la clé fait seule foi :
n’importe qui la détenant peut réclamer la licence. C’est acceptable quand la clé est remise
en main propre avec le matériel, mais c’est la seule protection.
claimable_until : 90 jours, et jamais avant la fin de validité de la licence — une licence
payée ne se ferme pas à la réclamation tant que le droit court.
Erreurs
| Code | Statut | Sens |
|---|---|---|
LICENSE_NOT_FOUND |
404 | licence inconnue |
LICENSE_NOT_HELD |
403 | vous n’êtes pas le titulaire |
LICENSE_NOT_RELEASABLE |
400 | statut incompatible (expirée, révoquée…) |
LICENSE_TRANSFER_LIMIT |
400 | plafond de transmissions atteint — validation nécessaire |
LICENSE_CONCURRENT_UPDATE |
400 | la licence vient de changer de titulaire |
2. POST /v1/licenses/:id/release/cancel
Annule la restitution tant que personne n’a réclamé. Une erreur de destinataire se rattrape sans passer par un administrateur.
// 200
{ "success": true, "data": { "license_id": "lic_…", "released": false } }
3. Côté client — la réclamation ne change pas
POST /v1/licenses/claim et POST /v1/licenses/claim-guest sont inchangés. Ils acceptent
désormais, en plus des cas existants, une licence rendue par son titulaire.
Si recipient_email a été désigné, seule cette adresse peut réclamer : toute autre
reçoit NOT_FOUND, volontairement indistinct d’une clé inconnue pour ne pas révéler qu’une
licence existe.
Un seul code d’erreur est nouveau :
| Code | Statut | Sens |
|---|---|---|
LICENSE_TRANSFER_INCOMPLETE |
200* | la bascule n’a pas tout déplacé — réessayez la même réclamation |
* dans l’enveloppe { success: false }. L’opération est idempotente : rejouer la
réclamation la termine. Ne demandez pas au client de recommencer la restitution.
4. Ce que la bascule préserve
- L’échéance. Le client hérite du temps restant, il ne gagne ni ne perd de jours.
- Le matériel et son accès distant. Le port, le vhost et le tunnel restent en place : aucune reconfiguration, c’est tout l’intérêt du parcours.
- L’historique. Le titulaire d’origine reste inscrit.
5. Limite de transmission
Une licence se transmet une fois par défaut (transfers_max). Au-delà, une validation
est nécessaire : c’est ce qui évite qu’une licence devienne un objet qui se revend hors des
canaux prévus. transfers_used vous permet de le montrer avant que l’utilisateur agisse.