Documentation J+SERVICES Guides Référence API

Contrat API — Panneau de gestion de consommation

Base : https://live.jmoai.net/api/v1/radius.

Le principe en une phrase : par routeur, le gérant active un panneau, choisit un mode (AUTO régulé, FUP seul, ou bridage manuel) et peut migrer les usagers de ses profils commerciaux vers la gestion cloud RADIUS ; le serveur décide et maintient, sans que l’application reste ouverte.

            ┌──────────── PUT /consumption-panel/{nasId} ────────────┐
            │ enabled + mode                                          │
            ▼                                                         │
  AUTO_REGULE ── toutes les 10 min : budget × usagers connectés ──► plafond du routeur
  FUP_ONLY    ── aucun plafond global ; chaque ticket suit le FUP de son profil RADIUS
  BRIDAGE     ── réglages manuels (mêmes champs que PUT /bandwidth/program)

  POST /consumption-panel/{nasId}/migration ── profils commerciaux (ou cochés)
      → vérification compte par compte → import cloud → retrait des seuls comptes prouvés

Le panneau n’écrit rien directement sur le routeur. Il oriente trois mécanismes existants, chacun gardant ses propres garanties : le moteur de bridage (seul à poser des files, sous le commentaire JSBRIDAGE), le FUP par ticket du worker RADIUS, et la bascule vérifiée.

Droits

Route Exige
GET /consumption-panel/{nasId} licence RADIUS + Suivi du Matériel
PUT /consumption-panel/{nasId} licence RADIUS + Suivi du Matériel + STARLINK X
GET /consumption-panel/{nasId}/profiles licence RADIUS
POST /consumption-panel/{nasId}/migration licence RADIUS

Mêmes droits, mêmes refus 403 (ENTITLEMENT_REQUIRED + required_feature) que le bridage : voir CONTRAT-API-BRIDAGE. Lire GET /clients/entitlements/bridage à l’ouverture de l’écran pour savoir quoi proposer. Les écritures sont limitées à 5 appels/minute ; le quota global reste 100 requêtes / 15 min par IP, partagé par TOUTE l’application — ne pas sonder l’état en boucle serrée (une lecture toutes les 30 s pendant une migration suffit).


GET /consumption-panel/{nasId}

L’état complet de l’écran, en un appel. Répond aussi pour un routeur jamais réglé (enabled: false, mode: null).

{
  "nas_id": "0b5c…",
  "routeur": "J+Pro Ax3",
  "enabled": true,
  "mode": "AUTO_REGULE",
  "revision": 3,
  "wan_down_mbps": 200,
  "wan_up_mbps": 20,
  "auto": {
    "plancher_confort_kbps": 1500,
    "plancher_survie_kbps": 512,
    "jours_confort_min": 21,
    "facteur_pointe": 2
  },
  "budget": {
    "quota_gb": 5000,
    "recharges_gb": 0,
    "billing_start_day": 9,
    "cycle_debut": "2026-09-09"
  },
  "decision": {
    "regime": "CONFORT",
    "brider": true,
    "debit_mbps": 37.5,
    "par_usager_kbps": 1500,
    "usagers_actifs": 25,
    "besoin_confort_mbps": 37.5,
    "besoin_survie_mbps": 12.8,
    "rythme_cycle_mbps": 30.9,
    "rythme_confort_mbps": 44.1,
    "reste_gb": 3120.4,
    "consomme_gb": 1879.6,
    "jour_gb": 142.3,
    "jours_restants_cycle": 17,
    "jours_confort_restants": 8,
    "rythme_observe_gb_jour": 144.6,
    "jours_tenables": 21.6,
    "capacite_adaptative": {
      "mode": "SIMULATION",
      "applique": false,
      "statut": "DONNEES_INSUFFISANTES",
      "echantillons": 0,
      "requis": 6
    },
    "change": false
  },
  "decision_le": "2026-09-22T19:42:00Z",
  "bridage": {
    "enabled": true,
    "mode": "ADAPTIVE",
    "limit_mbps": 200,
    "plafond_pilote_mbps": 37.5,
    "etat_souhaite": "ON",
    "etat_constate": "ON",
    "derniere_erreur": null
  },
  "fup": [
    {
      "profile_id": "7d1e…",
      "nom": "500F-4d",
      "prix_xof": 500,
      "debit_down_kbps": 50000,
      "debit_up_kbps": 15000,
      "fup_enabled": true,
      "fup_limit_mb": 5120,
      "fup_speed_down_kbps": 2048,
      "fup_speed_up_kbps": 512
    }
  ],
  "migration": {
    "scope": "ALL_COMMERCIAL",
    "profils": ["500F-4d", "200F-1d"],
    "statut": "ARMEMENT",
    "etapes": [{ "etape": "VERIFICATION", "etat": "PARTIEL", "retirables": 5002, "bloquants": 3 }],
    "maj_le": "2026-09-22T19:30:11Z"
  },
  "prerequis": [
    { "code": "BUDGET_DECLARE", "ok": true },
    { "code": "CAPACITE_LIEN", "ok": true },
    { "code": "MOTEUR_DE_FILES", "ok": true }
  ]
}

Comment l’afficher

Ce qui n’est PAS une erreur


PUT /consumption-panel/{nasId}

Active, change de mode ou désactive. Idempotent : rejouer le même corps ne produit que le même état. Réponse 200 : l’état complet, identique à GET.

Mode AUTO régulé

{
  "enabled": true,
  "mode": "AUTO_REGULE",
  "wan_down_mbps": 200,
  "wan_up_mbps": 20,
  "budget": { "quota_gb": 5000, "billing_start_day": 9 },
  "auto": {
    "plancher_confort_kbps": 1500,
    "plancher_survie_kbps": 512,
    "jours_confort_min": 21,
    "facteur_pointe": 2
  },
  "essential_classes": ["dns", "meta", "telegram", "x"],
  "essential_share": 0.25
}
Champ Obligatoire Règle
wan_down_mbps oui capacité descendante réelle du lien, > 0 et ≤ 10 000
wan_up_mbps non > 0 et ≤ 10 000
budget.quota_gb oui* volume du cycle (5 000 pour 5 To). *Facultatif si le forfait est déjà déclaré
budget.billing_start_day non 1–31, jour de renouvellement Starlink (défaut : celui déjà déclaré, sinon 1)
auto.plancher_confort_kbps non 256–20 000 (défaut 1 500)
auto.plancher_survie_kbps non 128–5 000 (défaut 512), jamais supérieur au plancher de confort
auto.jours_confort_min non 7–31 (défaut 21)
auto.facteur_pointe non 1–4 (défaut 2)
essential_classes, essential_share non identiques à PUT /bandwidth/program

Un paramètre hors bornes est refusé, jamais rabattu en silence : le gérant doit savoir que son réglage n’est pas celui qu’il croit.

Le forfait saisi est le même que CONTRAT-API-STARLINK-PRIORITY. Le lissage Starlink de ce routeur est suspendu (is_active: false) : le mode AUTO le remplace, deux pilotes ne peuvent pas écrire le même plafond. Les recharges déclarées s’ajoutent au budget et ne se reportent pas au cycle suivant.

Mode FUP seul

{ "enabled": true, "mode": "FUP_ONLY" }

Aucun plafond global : le programme de bridage éventuel est désactivé et le routeur nettoyé. Chaque ticket RADIUS suit le FUP de son profil — débit plein jusqu’au seuil, puis débit réduit. Le FUP se règle profil par profil sur PUT /profiles/{profileId}.

⚠️ Le seuil FUP est figé sur le ticket au moment de la vente. Modifier le FUP d’un profil s’applique aux tickets vendus ensuite, pas à ceux déjà en circulation. C’est volontaire : ce qui a été vendu ne change pas après coup. Les usagers encore authentifiés en local ne sont pas concernés par le FUP RADIUS tant qu’ils ne sont pas migrés.

Mode bridage manuel

{
  "enabled": true,
  "mode": "BRIDAGE",
  "bridage": {
    "mode": "BOTH",
    "limit_mbps": 10,
    "schedule_weekdays": [2, 3],
    "daily_quota_gb": 80,
    "user_daily_quota_gb": 5,
    "user_limit_mbps": 1
  }
}

bridage accepte exactement les champs de PUT /bandwidth/program (sans nas_id), avec les mêmes règles et les mêmes codes BW_* en cas de refus.

Désactiver

{ "enabled": false }

Retire le plafond piloté, désactive le programme de bridage créé pour le panneau (le routeur est nettoyé à la réconciliation suivante) et arrête le pilote AUTO. En mode FUP seul, les tickets déjà vendus gardent leur FUP.

Codes d’erreur (400 sauf mention)

Code Sens
PANEL_ENABLED_REQUIRED enabled absent ou non booléen
PANEL_MODE_INVALID mode inconnu
PANEL_WAN_INVALID capacité hors ]0, 10 000]
PANEL_WAN_REQUIRED mode AUTO sans wan_down_mbps
PANEL_BUDGET_REQUIRED mode AUTO sans budget, et aucun forfait déjà déclaré
PANEL_BILLING_DAY_INVALID jour de facturation hors 1–31
PANEL_AUTO_PARAM_INVALID réglage AUTO hors bornes ; details nomme le champ et ses bornes
PANEL_BRIDAGE_REQUIRED mode BRIDAGE sans objet bridage
PANEL_NAS_NOT_OWNED 404 — le routeur n’appartient pas au compte
BW_* refus du programme de bridage (voir CONTRAT-API-BRIDAGE)

Le mode AUTO — comment il décide

Toutes les 10 minutes, pour chaque panneau AUTO actif :

reste            = budget du cycle − consommé (WAN du routeur)
rythme(horizon)  = reste × 8 000 ÷ (heures restantes jusqu'à l'horizon × 3 600) × facteur de pointe
besoin confort   = usagers connectés × plancher de confort
besoin survie    = usagers connectés × plancher de survie

C’est la méthode du budget pacing des régies publicitaires et du fair usage des opérateurs mobiles : ce qui reste, divisé par le temps qui reste, recalculé en boucle. Une surconsommation de pointe fait baisser les plafonds suivants, une sous-consommation les relève : aucun modèle de demande à deviner. Le facteur de pointe reconnaît que le trafic n’est pas uniforme — le plafond ne mord qu’aux heures chargées.

Régime Quand Plafond du routeur Le gérant lit
LIBRE le rythme sur 30 j dépasse la capacité du lien aucun budget large
CYCLE le rythme sur 30 j couvre le besoin confort rythme sur 30 j le budget tient jusqu’au renouvellement
CONFORT 30 j passeraient sous le confort, 21 j non besoin confort trop d’usagers pour 30 j : qualité gardée, ≥ 3 semaines
PROTECTION_21J même 21 j passeraient sous le confort rythme sur 21 j débit réduit pour tenir 3 semaines
PLANCHER même la survie ne tient pas 21 j besoin survie, jamais moins budget insuffisant : recharge conseillée
BUDGET_EPUISE plus rien besoin survie jamais coupé : de quoi payer une recharge

Passé le 21e jour du cycle, la promesse des trois semaines est tenue : le régime reste CONFORT (qualité gardée) même si le budget doit finir avant le renouvellement.

Le plancher de confort de 1,5 Mb/s par usager permet la navigation, WhatsApp (messages, appels audio et vidéo) et la vidéo en définition standard — c’est le débit auquel les opérateurs mobiles plafonnent la vidéo sans dégrader l’usage courant. Le plancher de survie de 512 kb/s garde la messagerie et les appels audio. Sous tout plafond, le trafic essentiel (DNS, WhatsApp/Messenger/Instagram/Facebook, Telegram, X) passe en priorité 1 avec un débit garanti ; tout le reste attend son tour. Rien n’est jamais bloqué ni déconnecté.

Trois garde-fous à connaître pour ne pas s’étonner :

  1. Hystérésis de 20 % : dans un même régime, un plafond qui bouge de moins de 20 % n’est pas réécrit. Un changement de régime, lui, est toujours appliqué.
  2. Mesure aveugle ≠ zéro usager : si ni RADIUS, ni le routeur, ni l’agent QoE ne peuvent compter les connectés, la décision précédente reste en place.
  3. Délai de pose : jusqu’à 5 minutes entre la décision et le routeur.

Journal GET /bandwidth/activity?nas_id= : PANEL_ENABLED, PANEL_DISABLED, PANEL_MODE_CHANGED, AUTO_REGULATED, AUTO_RELEASED, PANEL_MIGRATION_STARTED, PANEL_MIGRATION_FINISHED. Une notification CONSUMPTION_PANEL_REGIME part une fois par régime et par jour en entrant dans CONFORT, PROTECTION_21J, PLANCHER ou BUDGET_EPUISE — jamais à chaque recalcul.


GET /consumption-panel/{nasId}/profiles

Les profils Hotspot du routeur, classés, pour l’écran de migration. Lit le routeur : prévoir quelques secondes.

{
  "nas_id": "0b5c…",
  "scope": "SELECTED",
  "profils": [
    {
      "nom": "default",
      "categorie": "DEFAUT",
      "raison": "PROFIL_SYSTEME",
      "prix": null,
      "usagers_locaux": 0,
      "selectionne": false
    },
    {
      "nom": "500F-4d",
      "categorie": "COMMERCIAL",
      "raison": "ENTETE_MIKHMON",
      "prix": 500,
      "usagers_locaux": 4212,
      "selectionne": true
    },
    {
      "nom": "staff",
      "categorie": "NON_COMMERCIAL",
      "raison": "AUCUNE_PREUVE_DE_VENTE",
      "prix": null,
      "usagers_locaux": 3,
      "selectionne": false
    }
  ]
}

Un profil est COMMERCIAL dès qu’une preuve de vente existe : en-tête Mikhmon avec prix ou validité (ENTETE_MIKHMON), métadonnées JSON de l’application avec un prix (METADONNEES_APP), ou prix au catalogue RADIUS (CATALOGUE_RADIUS). default et default-trial sont DEFAUT et ne sont jamais migrés. Les profils de sortie de bridage (fup-*) sont NON_COMMERCIAL.

Afficher une case à cocher par profil, pré-cochée sur selectionne. Griser DEFAUT.


POST /consumption-panel/{nasId}/starlink-reading

Le gérant saisit le consommé du cycle affiché par l’application Starlink. Le serveur le rapproche de sa propre mesure WAN au même instant et calibre le coefficient appliqué aux décisions. Mêmes droits que la lecture du panneau ; 5 appels/minute.

{ "consomme_gb": 1080 }

Réponse 200 :

{
  "nas_id": "0b5c…",
  "declare_gb": 1080,
  "mesure_gb": 1000,
  "ecart_pct": 8,
  "coefficient_brut": 1.08,
  "coefficient_calibrage": 1.08,
  "retenu": true,
  "raison": "CALIBRE"
}
raison Sens
CALIBRE relevé retenu ; lissé 50/50 avec le coefficient précédent
DECLARE_INFERIEUR_A_LA_MESURE Starlink affiche moins que nous : ramené à 1,00 (saisie à vérifier)
ECART_ANORMAL_PLAFONNE écart > 30 % : plafonné à 1,30 — appareils hors routeur ou erreur
MESURE_TROP_FAIBLE moins de 20 Go mesurés dans le cycle : relevé gardé, non retenu

Erreurs : PANEL_READING_INVALID (400), PANEL_BUDGET_REQUIRED (400, aucun forfait), PANEL_MEASURE_UNAVAILABLE (503, mesure du routeur indisponible — réessayer).

Proposer la saisie une fois par semaine : c’est ce qui rapproche le budget piloté de la facture Starlink (objectif d’écart ≤ 2 %).

POST /consumption-panel/{nasId}/migration

Le bouton « migrer vers la gestion cloud ».

{ "scope": "ALL_COMMERCIAL" }
{ "scope": "SELECTED", "profiles": ["500F-4d", "staff"], "retirer_local": true }
Champ Règle
scope ALL_COMMERCIAL (tous les profils commerciaux, rien d’autre) ou SELECTED
profiles requis avec SELECTED ; noms exacts de GET …/profiles ; non commerciaux acceptés
retirer_local défaut true. false = importer et vérifier sans rien retirer du routeur

Réponse 202 — la migration part en arrière-plan :

{
  "nas_id": "0b5c…",
  "statut": "EN_COURS",
  "scope": "ALL_COMMERCIAL",
  "profils": ["500F-4d", "200F-1d"],
  "suivi": "/consumption-panel/0b5c…"
}

202 veut dire « lancée », jamais « terminée ». Suivre migration.statut et migration.etapes sur GET /consumption-panel/{nasId}, toutes les 30 s environ.

Ce que fait la migration, dans l’ordre

  1. Sauvegarde du routeur, rapatriée et vérifiée — sinon rien ne se passe.
  2. Configuration RADIUS du routeur (use-radius=yes relu sur l’appareil).
  3. Vérification compte par compte des usagers des seuls profils choisis : chaque compte local est confronté à son ticket cloud et à FreeRADIUS. Les absents sont importés, le temps que le cloud offrait en trop (ticket importé avant d’être consommé) est réaligné, puis tout est relu.
  4. Retrait du routeur des seuls comptes prouvés, hors session en cours. Les autres restent en local et continuent de fonctionner.

États d’étape : OK, PARTIEL (il reste des comptes bloquants ou en session — relancer plus tard les retirera s’ils sont devenus prouvés), REFUSE, ECHEC (rien n’a été retiré). migration.statut: "ECHEC" s’accompagne d’un code : MIGRATION_BACKUP_FAILED, MIGRATION_DEPLOY_FAILED, MIGRATION_VERIFY_INCOMPLETE, MIGRATION_REMOVE_STALLED — détail dans CONTRAT-API-MIGRATION-RADIUS.

Codes d’erreur

Code Statut Sens
PANEL_MIGRATION_RUNNING 409 une migration est déjà en cours sur ce routeur
PANEL_SCOPE_INVALID 400 scope inconnu
PANEL_PROFILES_REQUIRED 400 SELECTED sans profil
PANEL_PROFILE_UNKNOWN 400 profil absent du routeur ; details.profils les nomme
PANEL_DEFAULT_PROFILE_FORBIDDEN 400 default ou default-trial coché ; details.profils
PANEL_NO_COMMERCIAL_PROFILE 400 ALL_COMMERCIAL mais aucun profil commercial sur le routeur
PANEL_ROUTER_UNREACHABLE 409 routeur sans accès distant

Ce que le serveur garantit

Ce qui n’est PAS une erreur

Écarts assumés