Documentation de l'API mbelon
Version 2.2 · mise à jour le 17 octobre 2026
L'API REST de mbelon permet à une plateforme de créer des appareils, de les verrouiller et déverrouiller, de suivre leur état et de consulter le portefeuille prépayé. Les corps de requête et de réponse sont en JSON (UTF-8). Adresse de base : /api/v1 sur le domaine de votre serveur mbelon.
Version Markdown de cette page
Principes
- Chaque requête est signée : aucune requête non signée n'est acceptée.
- Les messages d'erreur sont traduits (en-tête Accept-Language) ; le champ « code » ne change jamais et sert à programmer.
- Idempotence : sur les requêtes qui modifient, l'en-tête facultatif Idempotency-Key (8 à 64 caractères : lettres, chiffres, _ . : -) permet de renvoyer la même requête sans effet double, donc sans second débit. La clé est conservée 24 heures : le même contenu rejoue la même réponse ; la même clé avec un autre contenu répond 422 IDEMPOTENCY_KEY_REUSED ; une requête encore en cours répond 409 IDEMPOTENCY_IN_PROGRESS (réessayez dans quelques secondes).
- Limite : 120 requêtes par minute et par clé (réglable par mbelon). Au-delà : erreur 429.
Authentification et signature
Trois en-têtes sont obligatoires sur chaque requête.
- « corps » est le JSON compact de la requête : mbelon le recalcule à partir du JSON reçu, sans espaces et avec les clés dans l'ordre envoyé. Signez donc exactement la chaîne produite par JSON.stringify (ou json.dumps avec separators=(",", ":")) et envoyez cette même chaîne. Pour une requête sans corps (GET) : le texte {}.
- Une requête qui modifie ne peut être reçue qu'une fois avec la même signature (protection contre le rejeu).
- Vous pouvez limiter l'accès à une liste d'adresses IP (menu Intégration). C'est facultatif : sans liste, aucune adresse n'est refusée. Une autre adresse reçoit 403 API_IP_NOT_ALLOWED.
- Le secret de signature de la clé principale est le même pour vos appels et pour les webhooks (menu Intégration, ou Coffre de clés). Une clé à droits limités a son propre secret pour signer vos appels ; les webhooks restent signés avec le secret du compte.
| En-tête | Valeur |
|---|---|
| X-Mbelon-Api-Key | Votre clé d'API (menu Intégration). |
| X-Mbelon-Timestamp | Heure Unix en secondes. Écart toléré : 5 minutes. |
| X-Mbelon-Signature | HMAC-SHA256 en hexadécimal de « horodatage.corps » avec votre secret de signature. |
const crypto = require('crypto');
const body = JSON.stringify({ external_contract_id: 'CTR-2026-9011', device_type: 'ANDROID_MOBILE', device_imei_1: '358912345678901' });
const timestamp = String(Math.floor(Date.now() / 1000));
const signature = crypto.createHmac('sha256', SIGNING_SECRET).update(timestamp + '.' + body).digest('hex');
// En-tetes: X-Mbelon-Api-Key: API_KEY, X-Mbelon-Timestamp: timestamp, X-Mbelon-Signature: signatureimport hmac, hashlib, json, time
body = json.dumps({"external_contract_id": "CTR-2026-9011", "device_type": "ANDROID_MOBILE", "device_imei_1": "358912345678901"}, separators=(",", ":"))
timestamp = str(int(time.time()))
signature = hmac.new(SIGNING_SECRET.encode(), (timestamp + "." + body).encode(), hashlib.sha256).hexdigest()Clés à droits limités
Outre la clé principale du compte (tous les droits), vous pouvez créer des clés supplémentaires dont la valeur commence par « mbk_ », chacune avec son propre secret de signature, ses droits et, au choix, un vendeur fixe. La clé et son secret ne sont affichés qu'une fois. Une route interdite renvoie 403 API_SCOPE_DENIED.
| Droit | Routes |
|---|---|
| devices:read | GET /devices, GET /devices/:reference, GET /devices/:reference/enrollment, GET /devices/:reference/locations, GET /policies |
| devices:write | POST /devices/bind, PUT /devices/:reference/frp-accounts, POST /devices/:reference/enrollment/activation-code, POST /devices/:reference/enrollment/activation-password, POST /devices/:reference/enrollment/command |
| devices:control | POST /devices/:reference/action, POST /devices/:reference/policy, POST /devices/:reference/locate, POST /devices/:reference/message, POST /contracts/update-status |
| policies:write | POST, PUT, DELETE /policies |
| wallet:read | GET /wallet, GET /wallet/quote |
Enregistrer un appareil
POST /devices/bind enregistre l'appareil et son contrat de service. L'argent est débité du portefeuille à cet instant, la durée commence aujourd'hui, et rien n'est remboursé. Si le solde est insuffisant, rien n'est enregistré et l'API répond 402. Les conditions du contrat sont acceptées une fois pour le compte.
- access_number : numéro secret propre à cet appareil, affiché dans la réponse et consultable dans le portail. Il sert au mode vendeur strict.
- Un appareil dont le contrat est achevé peut être enregistré de nouveau pour une nouvelle vente (nouveau contrat, nouveau paiement).
- Mode test : tant que votre compte n'est pas en production, 2 appareils au plus peuvent être enrôlés, ils sont réellement verrouillés et déverrouillés, et l'enregistrement ne débite rien (le contrat est marqué « test »). Quand mbelon approuve la production, les appareils et contrats de test sont supprimés : enregistrez de nouveau vos vrais appareils.
- Si votre appel d'enregistrement expire sans réponse : l'enregistrement est tout ou rien (appareil, contrat et débit ensemble). Vérifiez avec GET /devices/{IMEI} et GET /wallet, ou renvoyez la même requête avec la même Idempotency-Key (aucun second débit).
- La durée service_years ne peut pas être prolongée après l'enregistrement pour le moment.
- La réponse contient aussi provisioning_qr_payload (l'adresse du QR code d'enrôlement). Pour obtenir l'image du QR code, les codes courts et la commande, sans jamais ouvrir le portail, voir « Enrôler l'appareil depuis votre plateforme ».
- Solde insuffisant : l'API répond 402 INSUFFICIENT_BALANCE, rien n'est enregistré. Si votre propre plateforme débite le vendeur à chaque appareil, faites afficher la même erreur chez le vendeur : il n'y a pas de portefeuille par vendeur chez mbelon, un seul portefeuille, celui du compte.
| Champ | Obligatoire | Description |
|---|---|---|
| external_contract_id | oui | Votre numéro de contrat (1 à 100 caractères). |
| device_type | oui | ANDROID_MOBILE, ANDROID_TABLET, ANDROID_TV ou ANDROID_OTHER. WINDOWS_PC et MACOS_PC figurent dans la liste, mais l'enregistrement répond pour le moment 422 DEVICE_TYPE_NOT_AVAILABLE (pas disponible pour le moment) : rien n'est enregistré ni débité. |
| device_imei_1 | téléphone | IMEI (15 chiffres). Une tablette ou un téléviseur peut utiliser device_serial à la place. |
| device_imei_2, device_serial | non | Second IMEI, numéro de série. |
| service_years | non | Durée du contrat de service, de 1 à 6 ans (1 par défaut). |
| device_name | non | Nom donné à l'appareil (80 caractères). |
| customer_ref, seller_ref, order_ref | non | Vos références (64 caractères) : client, vendeur, commande. |
| country | non | Pays de l'appareil (code à deux lettres) : le prix peut dépendre du pays. |
| policy_name ou policy_id | non | Profil de règles à appliquer. |
| offline_grace_period_hours, billing_mode, daily_rate_amount, currency | non | Réglages avancés du contrat. |
| frp_account_ids | non | Comptes Google autorisés après une remise à zéro pour cet appareil (identifiants numériques, cinq au plus, séparés par des virgules ou en liste). Voir la section « Comptes Google de protection ». |
POST /api/v1/devices/bind
{
"external_contract_id": "CTR-2026-9011",
"device_type": "ANDROID_MOBILE",
"device_imei_1": "358912345678901",
"device_name": "Tecno Spark de Awa",
"customer_ref": "CLIENT-42",
"seller_ref": "VENDEUR-7",
"order_ref": "CMD-2026-9",
"country": "CM",
"service_years": 2
}201
{
"mbelon_device_id": "0b6f...",
"device_name": "Tecno Spark de Awa",
"access_number": "mbd_K7M2QXW9T4PAB3NC8R5VYZDHE6",
"service_contract": { "years": 2, "amount": "6000.00", "currency": "XAF", "starts_at": "2026-10-09T10:00:00.000Z", "ends_at": "2028-10-09T10:00:00.000Z", "test": false },
"contract_id": "..."
}402
{ "statusCode": 402, "code": "INSUFFICIENT_BALANCE", "message": "Solde insuffisant ...", "params": { "needed": "3000.00", "balance": "1000.00", "currency": "XAF" } }Enrôler l'appareil depuis votre plateforme
Après POST /devices/bind, tout ce qu'il faut pour enrôler le téléphone s'obtient par l'API, sans que le vendeur ouvre le portail mbelon. Ces routes sont signées comme les autres, acceptent la référence de l'appareil (IMEI, numéro de série, identifiant ou numéro d'accès) et respectent le mode vendeur strict.
- status vaut PENDING (pas encore enrôlé), ENROLLED ou FAILED (une tentative a échoué : last_failure donne le code et l'heure). Une fois l'appareil enrôlé, qr vaut null.
- Ces mêmes informations sont dans la fiche de l'appareil (GET /devices/:reference), champ enrollment.
- Pour être prévenu sans interroger l'API, abonnez-vous aux webhooks device.enrolled, device.enroll_failed et device.enroll_expired (voir Webhooks). Chacun contient aussi customer_ref, seller_ref et order_ref (vos références, ou null) pour retrouver tout de suite la vente concernée.
- Choisir la méthode : le QR code ou le code d'activation court pour un téléphone neuf ou remis à zéro (sans ordinateur) ; le fichier prêt à ouvrir (…/enrollment/file) ou la commande pour un téléphone déjà utilisé, depuis un ordinateur. Toutes les adresses remises passent par le nom de domaine de mbelon, jamais par une adresse IP.
| Route | Droit | Rôle |
|---|---|---|
| GET /devices/:reference/enrollment | devices:read | État de l'enrôlement et, tant que l'appareil n'est pas enrôlé, le QR code : image PNG en base64, contenu (payload, l'objet que le QR code encode) et adresse (url). |
| POST /devices/:reference/enrollment/activation-code | devices:write | Crée un code d'activation court (XXXX-XXXX-XXXX, valable 24 h, un seul usage). Un nouveau code remplace le précédent. |
| POST /devices/:reference/enrollment/activation-password | devices:write | Corps { "password": "..." } : mot de passe d'activation de 5 à 12 lettres ou chiffres, valable 24 h, un seul usage. |
| POST /devices/:reference/enrollment/command | devices:write | Commande à lancer depuis un ordinateur relié au téléphone par câble (valable 72 h). L'ordinateur doit avoir adb (outils de plateforme Android de Google). |
| POST /devices/:reference/enrollment/file | devices:write | Corps { "os": "windows" } ou { "os": "unix" } (Linux, macOS), avec en option "device_address" : l'adresse de l'appareil sur le réseau Wi-Fi local, par exemple 192.168.1.20:37215 (adresse privée seulement ; sinon 400 ENROLL_FILE_ADDRESS_INVALID). Retourne un fichier fait pour CET appareil : device_id, os, filename, content_type, content (le texte du fichier), expires_at. À enregistrer tel quel puis à ouvrir sur l'ordinateur relié au téléphone : il télécharge lui-même adb s'il manque, trouve l'appareil (adresse Wi-Fi indiquée, sinon câble USB ; si plusieurs appareils sont branchés, il laisse choisir, et s'il n'en trouve aucun il demande l'adresse Wi-Fi), installe l'application, lance l'enrôlement (le téléphone propose d'abord la confirmation et accepte tout seul après 3 minutes au plus) et affiche le résultat en direct, avec un message clair si la connexion Internet du téléphone manque. Il ne contient aucune clé de compte. |
{
"device_id": "0b6f...",
"status": "PENDING",
"enrolled_at": null,
"last_failure": null,
"activation_code_pending": true,
"activation_code_expired": false,
"qr": { "image_png_base64": "iVBORw0...", "payload": { "...": "..." }, "url": "https://.../v1/qr/0b6f..." }
}Comptes Google de protection (FRP) par appareil
Après une remise à zéro non autorisée, l'appareil ne peut être reconfiguré que par un compte Google autorisé. Trois niveaux se cumulent, sans doublon : les comptes de mbelon (obligatoires, sur tous les appareils), ceux du compte partenaire (réglages du portail) et ceux de l'appareil. Tant qu'un niveau n'est pas renseigné, il est simplement ignoré ; quand plusieurs le sont, tous s'appliquent en même temps. Il n'existe aucune option pour en interdire un.
- À l'enregistrement : le champ frp_account_ids de POST /devices/bind.
- Plus tard : PUT /devices/:reference/frp-accounts avec { "frp_account_ids": "123456789012345678901,987654321" } (droit devices:write). Une chaîne vide retire les comptes de l'appareil.
- Chaque identifiant est un nombre de 5 à 30 chiffres (l'identifiant numérique du compte Google, pas l'adresse e-mail), cinq au plus par appareil. Sinon : 400 VALIDATION_FRP_ACCOUNTS.
- Limite d'Android : cette protection n'existe qu'à partir d'Android 11. Sur une version plus ancienne, les autres protections de mbelon restent actives.
- Où trouver l'identifiant numérique : c'est l'identifiant stable du compte Google (le champ « sub » d'une connexion Google OpenID). On le lit, par exemple, dans la réponse de Google après une connexion de test avec ce compte (outil Google OAuth 2.0 Playground, jeton d'identité ou appel userinfo). Vérifiez toujours le résultat sur un appareil de test avant de l'utiliser.
- Essayez d'abord sur un appareil de test : un identifiant faux peut empêcher de reconfigurer l'appareil après une remise à zéro.
Consulter les appareils
- GET /devices : paramètres q (IMEI, série, nom, références), filter (ALL, ACTIVE, OVERDUE, PAID_OFF, NOT_ENROLLED, OFFLINE), limit (1 à 200), offset.
- GET /devices/:reference : la référence est l'IMEI, le numéro de série ou l'identifiant (en mode vendeur strict : le numéro d'accès).
- La fiche contient notamment status, enrolled, last_sync_at, applied_state et command : state vaut NONE, PENDING (le téléphone ne s'est pas reconnecté), FETCHED (reçue, en attente de confirmation) ou APPLIED.
Commander un appareil
- POST /devices/:reference/action avec { "action": "..." } parmi FORCE_LOCK, FORCE_UNLOCK, LIBERATE (libération définitive), PING, REPORT_LOST, REPORT_STOLEN, REPORT_FOUND, TEST_LOCK, TEST_UNLOCK.
- Essai avant la remise au client : TEST_LOCK verrouille l'appareil pour l'essayer, TEST_UNLOCK le déverrouille. Le deuxième n'est possible que pendant un essai (sinon 409 TEST_UNLOCK_NOT_ACTIVE) et le premier seulement sur un appareil enrôlé, actif et non déclaré perdu (sinon 409 TEST_LOCK_NOT_ALLOWED). Rien ne se déverrouille tout seul. Le contrat, l'échéance et le délai hors ligne ne changent pas. La fiche de l'appareil indique test_lock: true pendant l'essai.
- POST /contracts/update-status : met à jour l'état d'un contrat. contract_status vaut ACTIVE (déverrouillé), OVERDUE (se verrouille) ou PAID_OFF (contrat soldé). Champs facultatifs : lock_level, display_message, last_payment_amount, amount_due et l'échéance (voir ci-dessous).
- lock_level n'est lu qu'avec OVERDUE : SOFT n'applique que les restrictions légères du profil, FULL (par défaut) affiche l'écran de verrouillage. Un appareil déclaré perdu ou volé est toujours en FULL. display_message (500 caractères au plus) est affiché sur l'écran verrouillé. amount_due est le montant à payer, affiché dans les rappels et sur l'écran du client.
- Échéance (avec ACTIVE) : paid_duration { value, unit: hours ou days } ajoute la durée à la fin de la période en cours (starts_at, facultatif, donne l'heure de départ) et le téléphone se verrouille à l'heure exacte, même hors ligne. Autre possibilité : valid_until avec lock_at_valid_until: true pour une date de votre choix. N'envoyez pas valid_until avec paid_duration. Sans lock_at_valid_until, valid_until est seulement informatif : mbelon ne verrouille pas à cette date, c'est à vous d'envoyer OVERDUE.
- PAID_OFF libère l'appareil à lui seul (tout de suite s'il est joignable, sinon à sa prochaine synchronisation). L'action LIBERATE donne le même résultat avec un ordre immédiat ; elle n'est pas obligatoire après PAID_OFF.
- FORCE_UNLOCK est refusé (409 DEVICE_REPORTED_LOST) tant que l'appareil est déclaré perdu ou volé : envoyez d'abord REPORT_FOUND.
- Un ordre arrive en quelques secondes si le téléphone est allumé et connecté ; sinon il l'applique à sa prochaine synchronisation (toutes les 15 minutes environ). Suivez le champ command de la fiche.
POST /api/v1/contracts/update-status
{ "external_contract_id": "CTR-2026-9011", "contract_status": "OVERDUE", "lock_level": "FULL", "display_message": "Echeance en retard" }Profils de règles
- GET /policies, POST /policies, PUT /policies/:id, DELETE /policies/:id, POST /policies/:id/default.
- POST /devices/:reference/policy avec policy_id ou policy_name (policy_id: null pour revenir au profil par défaut).
Écran verrouillé : appels et applications autorisées
Quand un téléphone est verrouillé, le numéroteur reste autorisé par défaut : le client peut appeler et composer des codes (par exemple pour acheter un forfait). Le magasin d'applications est fermé par défaut. Ces réglages se font dans un profil de règles (POST /policies), que le partenaire peut aussi affecter à un appareil (POST /devices/:reference/policy).
- Ces règles n'agissent que pendant le verrouillage, sauf disable_outgoing_calls qui vaut aussi hors verrouillage.
| Règle | Effet sur un appareil verrouillé |
|---|---|
| lock_allow_dialer | true (par défaut) : le numéroteur reste utilisable. false : il est fermé. |
| lock_allow_play_store | false (par défaut) : le magasin d'applications est fermé. true : il reste ouvert. |
| lock_allowed_packages | Liste d'applications qui restent utilisables malgré le verrouillage (par exemple une application de paiement Mobile Money). |
| disable_outgoing_calls | true : interdit les appels sortants (en plus du verrouillage). |
Portefeuille
- GET /wallet : balance, currency, quota (plafond d'appareils en cours ou null), running_devices.
- GET /wallet/quote?country=CM&years=2 : prix du contrat dans la devise du portefeuille.
- Vérifiez le solde avant une vente et prévoyez la réponse 402 : elle n'a aucun effet sur le reste de votre activité.
- En production, chaque appareil enregistré est débité du prix annuel (prix du pays, sinon prix général) × service_years, converti dans la devise du portefeuille et arrondi vers le haut ; GET /wallet/quote utilise exactement le même calcul. Seul un changement de prix par mbelon entre le devis et l'enregistrement peut modifier le montant.
- En mode test, aucun débit et aucun solde n'est nécessaire.
Mode vendeur strict
Pour une plateforme à plusieurs vendeurs. Activez le mode dans le menu Intégration, ou liez une clé à un vendeur.
- Chaque appel annonce son vendeur dans l'en-tête X-Mbelon-Seller (inutile avec une clé liée à un vendeur).
- L'appareil créé porte ce vendeur ; il est désigné par son numéro d'accès, jamais par l'IMEI.
- Un appareil d'un autre vendeur, ou un appel sans vendeur, répond 404 : jamais 403.
- Les autres routes d'un appareil (enrôlement, comptes Google, localisation, actions) obéissent à la même règle : appareil d'un autre vendeur, ou appel sans vendeur, répond 404.
Vendeur indépendant
Chaque vendeur de votre plateforme peut agir seul, par l'API, sur ses propres appareils, comme le partenaire le fait depuis son compte. Il n'a pas besoin du portail mbelon, et il ne voit jamais les appareils d'un autre vendeur.
- Un vendeur ne peut pas régler les restrictions du téléphone (profils de règles) : POST /devices/:reference/policy lui répond 403 API_SCOPE_DENIED. Ce droit reste au partenaire.
- Le mode vendeur strict ou une clé liée à un vendeur impose cette séparation : un appareil d'un autre vendeur répond toujours 404.
- Le client final voit l'application du téléphone ; rien de plus n'est présenté au vendeur ni au client sur l'origine du service.
| Le vendeur veut | Appel (clé liée au vendeur ou en-tête X-Mbelon-Seller) |
|---|---|
| Enregistrer un appareil | POST /devices/bind |
| Enrôler l'appareil (QR, codes, commande) | GET /devices/:reference/enrollment, POST .../enrollment/activation-code, activation-password, command |
| Verrouiller, déverrouiller | POST /devices/:reference/action avec FORCE_LOCK ou FORCE_UNLOCK |
| Déclarer perdu, volé, retrouvé | POST /devices/:reference/action avec REPORT_LOST, REPORT_STOLEN ou REPORT_FOUND |
| Localiser | POST /devices/:reference/locate, puis GET /devices/:reference/locations |
| Ajouter un compte Google de protection | PUT /devices/:reference/frp-accounts |
| Suivre l'appareil | GET /devices, GET /devices/:reference |
Webhooks
Si vous renseignez une adresse HTTPS publique, mbelon vous envoie des événements en POST JSON.
- En-têtes : X-MBELON-EVENT, X-MBELON-DELIVERY, X-MBELON-TIMESTAMP, X-MBELON-SIGNATURE.
- Signature : HMAC-SHA256 hexadécimal de « X-MBELON-TIMESTAMP.corps » avec le secret du compte.
- Répondez par un code 2xx en moins de 10 secondes. Sinon mbelon réessaie après 1 min, 5 min, 30 min, 2 h puis 12 h (6 essais au total) : traitez chaque livraison une seule fois grâce à son identifiant.
- La livraison est « au moins une fois » : un même événement peut arriver plusieurs fois et dans le désordre. L'identifiant à utiliser contre les doublons est X-MBELON-DELIVERY, identique au champ id du corps ; rétablissez l'ordre avec created_at.
- Contenu de data : device_id, imei, serial et state pour device.locked, device.unlocked et device.released ; device_id, imei, serial, model, android_version et via pour device.enrolled et device.reenrolled ; device_id, imei, serial, type et detail pour device.tamper ; device_id, imei, serial, last_sync_at et hours_without_news pour device.offline.
- Votre numéro de contrat (external_contract_id) n'est pas dans le contenu : retrouvez l'appareil par device_id (renvoyé par l'enregistrement sous le nom mbelon_device_id) avec GET /devices/{device_id}, qui renvoie aussi le contrat.
- device.offline ne verrouille jamais le téléphone : c'est seulement une information. Il n'y a pas d'événement dédié à la désinstallation ou à la remise à zéro : surveillez device.offline, device.tamper (ADMIN_REMOVED), device.reenrolled et GET /fleet (états REMOVED, RESET, TAMPERED, SILENT).
- Contenu de data pour les nouveaux événements : device_id, imei, serial, reason, via (QR, CODE, IMEI ou USB) et at pour device.enroll_failed ; device_id, imei, serial et expired_at pour device.enroll_expired.
| Événement | Quand |
|---|---|
| device.enrolled | Le téléphone est enrôlé. |
| device.enroll_failed | Une tentative d'enrôlement a échoué (champ reason : code court périmé, jeton invalide, appareil déjà enrôlé, limite d'essais, etc.). Au plus un événement toutes les 10 minutes par appareil. |
| device.enroll_expired | Le code d'activation court de l'appareil est arrivé à échéance sans enrôlement. |
| device.reenrolled | Le téléphone est enrôlé de nouveau. |
| device.locked | Le téléphone confirme son verrouillage. |
| device.unlocked | Le téléphone confirme son déverrouillage. |
| device.released | Le téléphone confirme sa libération. |
| device.offline | Le téléphone ne s'est plus connecté depuis trop longtemps. |
| device.tamper | Une manipulation a été détectée (champ type : SIM_CHANGED, CLOCK_TAMPER, CLOCK_SKEW, ADMIN_REMOVAL_ATTEMPT, ADMIN_REMOVED, INTEGRITY_FAILED, ROOT_DETECTED). |
| contract.reminder | Un rappel de paiement est dû (voir ci-dessous). |
| webhook.test | Test envoyé depuis le menu Intégration. |
{ "id": "delivery-id", "event": "device.locked", "created_at": "2026-10-09T10:00:00.000Z",
"data": { "device_id": "0b6f...", "imei": "358912345678901", "serial": null, "state": "OVERDUE" } }Parc, santé, localisation et rappels
Routes ajoutées pour suivre un parc d’appareils (entreprises comme vendeurs). Elles se signent comme les autres. Les droits « devices:read » (lecture) et « devices:control » (localiser, message) de vos clés s’appliquent.
- Webhook « contract.reminder » : envoyé une fois par créneau de rappel avant l’échéance, avec l’appareil, l’échéance et le montant. Vous envoyez alors le rappel à votre client avec vos propres moyens.
- GET /health et GET /public/service-levels : santé du service et objectifs mesurés (disponibilité, délai de réponse, appareils qui répondent). Publics, sans donnée de compte.
| Route | Rôle |
|---|---|
| GET /fleet | Santé de chaque appareil : ACTIVE, SILENT (ne répond plus), REMOVED, RESET, TAMPERED (manipulé), NOT_ENROLLED, RELEASED. Paramètres : state, q, assigned (yes/no). |
| GET /devices/:reference/health | Santé d’un appareil, raisons et derniers événements. |
| POST /devices/:reference/locate | Demande de localisation. Le téléphone est réveillé aussitôt par une notification et envoie sa position ; lisez-la avec GET /devices/:reference/locations (de quelques secondes à quelques minutes selon la connexion du téléphone). Le téléphone affiche toujours un message discret et chaque demande est inscrite au journal. |
| GET /devices/:reference/locations | Positions reçues (conservées selon les réglages du compte). |
| POST /devices/:reference/message | Message court affiché sur l’écran du téléphone (champ text, 300 caractères au plus). |
| GET /reminders/upcoming | Contrats arrivant à échéance (paramètre days, 1 à 90, 7 par défaut), pour envoyer vos propres rappels (SMS, WhatsApp, e-mail) à vos clients. |
Protection des appareils : score et liste des protections
Chaque appareil Android (téléphone, tablette, télévision, borne) porte un score de protection de 0 à 100 et la liste exacte de ce qui est en place. Les champs ci-dessous figurent dans GET /devices, GET /devices/:reference, GET /fleet et GET /devices/:reference/health. Ils sont calculés à partir de ce que l’appareil annonce : ils ne remplacent pas un essai sur l’appareil.
- Répartition du score : contrôle de l’appareil 40, protection contre l’effacement par les touches 30, authenticité de l’application 15, blocage du mode développeur 10, contact récent avec le serveur 5.
- Les huit protections : contrôle de l’appareil, désinstallation de l’application bloquée, remise à zéro depuis les réglages bloquée, mode développeur et débogage bloqués, mode sans échec bloqué, ajout d’utilisateurs bloqué, protection contre l’effacement par les touches, application authentique.
- GET /fleet ajoute counts.low_protection : le nombre d’appareils enrôlés à protection faible.
- Les transferts de fichiers par câble USB ne sont jamais bloqués par mbelon : le client reste libre de mettre ses fichiers sur son appareil. Seul un partenaire peut choisir de les bloquer, par une règle de son profil.
| Champ | Contenu |
|---|---|
| protection_score | Nombre entier de 0 à 100. |
| protection_rating | HIGH (85 et plus), MEDIUM (60 à 84) ou LOW. Toujours LOW si l’application ne contrôle plus l’appareil ou si l’application est jugée altérée. |
| protection_gaps | Liste de codes : ce qui manque. Liste vide = rien à corriger. |
| protection_checks | Huit protections, chacune avec un état : ON (vérifiée par l’appareil), OFF, UNKNOWN (pas encore vérifiée) ou WORKSHOP (ouverte par le mode atelier). |
| protection_rules | Règles du partenaire appliquées par l’appareil : applied, skipped (non prises en charge par cet appareil) et failed (refusées). |
| debug_status | LOCKED, WORKSHOP ou OPEN : état réel du blocage du mode développeur et du débogage, relu par l’appareil à chaque synchronisation. |
Protection : codes de « protection_gaps » et que faire
| Code | Signification | Que faire |
|---|---|---|
| NOT_ENROLLED | L’appareil n’est pas enrôlé. | Terminer l’enrôlement (QR code sur l’écran de bienvenue d’un appareil neuf ou remis à zéro). |
| NOT_DEVICE_OWNER | L’application ne contrôle pas l’appareil. | Remettre l’appareil à zéro et l’enrôler de nouveau. |
| FRP_NO_ACCOUNT | Aucun compte Google autorisé n’est configuré. | Portail > Réglages : renseigner les comptes autorisés après une remise à zéro. Essayer d’abord sur un appareil de test. |
| FRP_FAILED | Android a refusé la protection contre l’effacement. | Vérifier l’identifiant du compte autorisé et la version d’Android. |
| FRP_UNSUPPORTED | Cette version d’Android n’offre pas la protection par compte. | Aucune action : les autres protections restent actives. |
| FRP_UNKNOWN, DEBUG_UNKNOWN, INTEGRITY_UNKNOWN | L’appareil n’a pas encore annoncé cette information. | Attendre la prochaine synchronisation ; mettre l’application à jour si cela persiste. |
| DEBUG_OPEN | Le blocage du mode développeur ne tient pas, ou le mode atelier est ouvert. | Hors mode atelier, l’application le remet en place à la synchronisation suivante. |
| INTEGRITY_BAD | L’application sur l’appareil ne semble pas authentique. | Contrôler l’appareil physiquement. |
| OFFLINE | L’appareil n’a pas contacté le serveur depuis longtemps. | Vérifier l’appareil (éteint, sans réseau, batterie). |
Réglages du compte qui influencent vos appareils
- Délai sans nouvelles (menu Réglages, 72 heures par défaut) : au-delà, mbelon envoie l'événement device.offline à votre webhook. Il ne verrouille pas le téléphone.
- Vérification régulière de votre plateforme (menu Réglages) : si vous ne pouvez pas envoyer de notifications, mbelon peut interroger votre adresse toutes les 6 ou 12 heures (0 = désactivé). L'adresse doit contenir {contract_id}, remplacé par votre numéro de contrat ; réponse attendue : { "contract_status": "ACTIVE" ou "OVERDUE" ou "PAID_OFF", "display_message": facultatif, "lock_level": facultatif }. Inutile si vous appelez déjà POST /contracts/update-status.
- Comptes Google autorisés après une remise à zéro (menu Réglages) : votre compte s'ajoute à celui de mbelon, il ne le remplace pas ; la liste en compte cinq au plus. Saisissez l'identifiant numérique du compte Google (5 à 30 chiffres), jamais l'adresse e-mail : un identifiant faux peut bloquer le téléphone pour tout le monde. Essayez d'abord sur un téléphone d'essai sous Android 11 ou plus ; avant Android 11 cette protection n'existe pas.
- Coffre de clés (menu Intégration) : permet de revoir votre clé d'API et votre secret avec un code de 4 à 8 chiffres que vous choisissez. Ce sont les mêmes valeurs que « Clé d'API et secret ». Cinq essais ratés bloquent le coffre 30 minutes.
- Bac à sable (menu Bac à sable) : une clé de test avec deux faux appareils, un à jour et un en retard, et un bouton de remise à zéro. Aucun téléphone réel n'y est verrouillé : pour un essai réel, utilisez le mode test (2 appareils, sans débit).
Données de l'appareil et vie privée
- L'application envoie à mbelon : identifiants de l'appareil (IMEI, numéro de série, modèle, version d'Android), état de fonctionnement et de protection, événements de manipulation (changement de carte SIM, horloge) et la liste des applications installées pour appliquer vos règles.
- La position n'est demandée que par le partenaire ou quand l'appareil est déclaré perdu ou volé, selon les réglages du compte (conservation de 1 à 365 jours, 30 par défaut, réglable par pays). Le téléphone affiche toujours un message et chaque demande est inscrite au journal.
- L'application ne demande pas l'accès aux contacts, aux messages, au journal des appels, à la caméra ni au micro.
- Vous restez responsable d'informer votre client et de recueillir son accord pour le contrat de crédit : mbelon ne fournit pas de texte de consentement pour l'acheteur.
État ordonné et état réel d’un appareil
Le champ status de la fiche d’un appareil est l’ordre donné par vous (ACTIVE, OVERDUE, PAID_OFF). L’état réel est celui que le téléphone confirme lui-même : il peut différer tant que le téléphone ne s’est pas reconnecté. Ne considérez jamais un appareil comme verrouillé uniquement parce que status vaut OVERDUE.
- Verrouiller puis déverrouiller à la main (FORCE_LOCK, FORCE_UNLOCK) ne change jamais l’échéance de paiement du contrat : vous pouvez tester le verrouillage à tout moment, même si le client est à jour.
- REPORT_FOUND n’est accepté que pour un appareil déclaré perdu ou volé (sinon 409 DEVICE_NOT_REPORTED) ; REPORT_LOST et REPORT_STOLEN sont refusés (409 DEVICE_ALREADY_REPORTED) si l’appareil est déjà déclaré, sauf le passage de perdu à volé.
- Le webhook device.locked (ou device.unlocked) n’est envoyé que lorsque le téléphone confirme le changement.
- Un téléphone éteint, sans connexion ou dont l’application est endormie ne se verrouille qu’à sa prochaine connexion, sauf si l’échéance exacte est utilisée : le téléphone se verrouille alors tout seul à l’heure prévue, même hors ligne.
| Champ | Sens |
|---|---|
| status | Ordre donné (ACTIVE, OVERDUE, PAID_OFF). |
| applied_state | État confirmé par le téléphone (ACTIVE ou OVERDUE), null tant qu’il n’a rien confirmé. |
| command.state | PENDING : ordre envoyé, le téléphone ne s’est pas reconnecté depuis. FETCHED : le téléphone a reçu l’ordre, sa confirmation est attendue. APPLIED : ordre appliqué et confirmé par le téléphone. NONE : aucun ordre. |
| status_changed_at | Date du dernier changement d’ordre. |
| last_sync_at | Dernière connexion du téléphone. |
Diagnostic d’un appareil : pourquoi il n’est pas verrouillé
La fiche d’un appareil (GET /devices/:reference) contient les champs ci-dessous. Chaque problème a un code stable et un niveau : critical, warning ou info.
| Champ | Sens |
|---|---|
| diagnosis | Liste de { code, level } : les problèmes connus. Vide : rien à signaler. |
| ready_to_deliver | true si le téléphone a envoyé son rapport et qu’aucun problème critique n’est signalé : il peut être remis au client. |
| health_report | Réglages vus par le téléphone : sdk, flags (battery_unrestricted, notifications_enabled, location_permission, location_enabled, alarm_scheduled, exact_alarm_allowed, app_control_blocked, service_running), last_locate_error, standby_bucket, alarm_due_at. |
| health_at | Date du dernier rapport. |
Diagnostic : codes et que faire
| Code | Niveau | Signification et que faire |
|---|---|---|
| SILENT | critical | Aucune nouvelle depuis plus de 6 heures (ou jamais). Téléphone éteint, sans connexion ou application endormie : demander au client d’ouvrir l’application et de régler la batterie. |
| LOCK_UNCONFIRMED | critical | Ordre de verrouillage donné depuis plus de 10 minutes sans confirmation. Le téléphone se verrouille à sa prochaine connexion ou à l’ouverture de l’application. |
| NOT_DEVICE_OWNER | critical | L’application ne contrôle pas l’appareil : le remettre à zéro puis l’enrôler correctement. |
| NO_HEALTH_REPORT | info | Aucun rapport de santé : application pas encore à jour (2.9.3 ou plus) ou pas encore synchronisée. |
| BATTERY_RESTRICTED | critical | L’économie de batterie peut endormir l’application. Régler : Paramètres, Batterie, l’application, « Sans restriction ». |
| SERVICE_STOPPED | critical | Le service de veille permanent est arrêté ; il redémarre à la prochaine synchronisation ou à l’ouverture de l’application. |
| ALARM_NOT_SCHEDULED | critical | L’alarme d’échéance n’est pas programmée : pas de verrouillage hors ligne à l’heure. Ouvrir l’application la reprogramme. |
| EXACT_ALARM_DENIED | warning | Android refuse les alarmes exactes : le verrouillage hors ligne peut avoir quelques minutes de retard. |
| NOTIFICATIONS_OFF | warning | Notifications coupées sur le téléphone. |
| APP_CONTROL_OPEN | warning | Le client peut encore arrêter de force l’application ou effacer ses données ; la protection se pose à la prochaine synchronisation. |
| LOCATION_PERMISSION | warning | Autorisation de localisation manquante ; accordée à la prochaine synchronisation. |
| LOCATION_OFF | warning | Localisation éteinte sur le téléphone ; l’application la rallume à la prochaine demande (fiable à partir d’Android 11, selon le modèle sur Android 9 et 10). |
| LOCATE_FAILED | warning | La dernière demande de position a échoué (voir « Localisation : raisons d’échec »). |
Webhooks : device.silent et device.lock_unconfirmed
Deux événements préviennent votre plateforme avant que le client ne s’en aperçoive. Chacun est envoyé une seule fois par épisode.
| Événement | Quand | Champs utiles |
|---|---|---|
| device.silent | Aucune nouvelle du téléphone depuis 6 heures. Réarmé dès qu’il se reconnecte. | device_id, imei, serial, customer_ref, seller_ref, order_ref, hours |
| device.lock_unconfirmed | Ordre de verrouillage donné depuis plus de 10 minutes et non confirmé par le téléphone. | device_id, imei, serial, customer_ref, seller_ref, order_ref, ordered_at, minutes_since_order, command_state |
Applications installées et applications autorisées
Vous pouvez demander la liste des applications installées sur un téléphone, puis choisir lesquelles le client peut ouvrir quand son téléphone est verrouillé (par exemple l’application de paiement mobile). Elles apparaissent sur l’écran de verrouillage sous le titre « Applications disponibles ». Le magasin d’applications n’est pas proposé pendant le verrouillage. Informez vos clients dans votre contrat que la liste des applications peut être consultée.
| Route | Rôle |
|---|---|
| POST /devices/:reference/apps/request | Demande la liste au téléphone (droit devices:control). |
| GET /devices/:reference/apps | Liste reçue : apps (package, label), requested_at, received_at, pending (droit devices:read). |
| GET /lock-apps | packages (applications autorisées du compte) et known (applications vues sur vos téléphones). Droit devices:read. |
| PUT /lock-apps | Remplace la liste avec { "packages": ["com.exemple.paiement"] }. Droit policies:write ; refusé en mode vendeur strict (403 API_SCOPE_DENIED). |
Suivi régulier de la position et phrase de contrat (version 2.3)
Suivi : POST /devices/:reference/locate accepte un corps { "interval_minutes": 60, "duration_hours": 72 }. Rythmes permis : 30, 60, 120, 180 ou 360 minutes ; durée de 1 à 720 heures (30 jours), 720 par défaut. Le téléphone lit sa position tout seul à ce rythme, garde jusqu’à 24 lectures sans réseau et les envoie au retour du réseau. Le suivi s’arrête par POST /devices/:reference/locate/stop, à la libération de l’appareil, à la fin de la durée, et au déverrouillage si l’appareil était en retard au démarrage du suivi. GET /devices/:reference/locations renvoie en plus tracking : { active, interval_minutes, until, started_at, started_by, stop_on_unlock }.
Chaque demande de localisation est tentée trois fois au plus ; ensuite elle est fermée et last_result donne la raison.
Phrase de contrat : texte affiché dans la notification permanente du téléphone du client, dans sa langue (fr, en, es, zh, hi, ar). Une phrase par compte (GET/PUT /contract-notice) et une par vendeur (GET/PUT /contract-notice/sellers/:seller, liste sur /contract-notice/sellers) ; une clé de vendeur strict utilise /contract-notice/mine. Corps : { "texts": { "fr": "...", "en": "..." } }. 200 caractères au plus par langue, sans lien ; une langue vide est retirée. Priorité : vendeur, puis compte, puis phrase par défaut de mbelon. Les appareils en mode entreprise affichent toujours un texte neutre (« Appareil géré par … »). À n’écrire que si le contrat du client le prévoit.
- Droits : lecture avec devices:read, écriture avec policies:write (devices:write pour /contract-notice/mine), arrêt du suivi avec devices:control.
| Code d’erreur | Sens |
|---|---|
| LOCATION_TRACK_INVALID | Rythme ou durée de suivi non permis (400). |
| CONTRACT_NOTICE_INVALID | Phrase refusée : langue inconnue, plus de 200 caractères, lien, ou identifiant de vendeur invalide (400). |
| SELLER_REQUIRED | La route /contract-notice/mine demande une clé liée à un vendeur (403). |
Localisation : raisons d’échec
GET /devices/:reference/locations renvoie locations, pending et last_result. Quand le téléphone ne peut pas donner sa position, last_result vaut { "ok": false, "reason": "...", "at": "..." }.
- Le réglage de localisation discrète (menu Sécurité) est activé par défaut depuis la version 2.3 : aucun message n’est affiché sur le téléphone et la demande reste inscrite dans le journal. Mentionnez la localisation par écrit dans votre contrat. Si le téléphone avait coupé sa localisation, l’application l’allume pour la lecture puis la remet dans son état d’avant.
| reason | Sens |
|---|---|
| NO_PERMISSION | Autorisation de localisation absente. |
| LOCATION_OFF | Localisation éteinte sur le téléphone. |
| NO_PROVIDER | Aucun moyen de localisation disponible. |
| TIMEOUT | Aucune position trouvée à temps (intérieur, signal faible). |
| UNKNOWN | Raison inconnue. |
Application 2.9.5 : notification unique et localisation
- Une seule notification permanente : la phrase de contrat (la vôtre, sinon celle de mbelon) et, pour un appareil à crédit, la prochaine échéance. Toucher la notification ouvre l’écran du client. Les rappels de paiement ne changent pas.
- Appareil en mode entreprise : texte neutre « Appareil géré par … », jamais de texte de paiement.
- Localisation : seulement à la demande du partenaire ou du vendeur, et automatiquement pour un appareil déclaré perdu ou volé. Si le téléphone avait coupé sa localisation, l’application l’allume, lit la position, l’envoie puis remet l’état d’avant. Les messages d’Android du type « votre organisation a accès… » sont ceux du système et ne peuvent pas être modifiés.
- Sur itel, Tecno et Infinix : désactivez l’optimisation de batterie pour l’application et autorisez le démarrage automatique, sinon le système peut arrêter l’application et le téléphone cesse de se synchroniser.
Application 2.9.3 : comportement et réglages
- Un service de veille permanent garde l’application vivante et relance la synchronisation ; il redémarre au démarrage du téléphone et après un arrêt.
- Le téléphone verrouillé vérifie le paiement tout seul (toutes les 2 minutes, et dès que le réseau revient) : le client n’a pas besoin d’appuyer sur « Vérifier ».
- Les autorisations et notifications sont accordées par l’application et ne peuvent pas être retirées par le client ; « Forcer l’arrêt » et « Effacer les données » sont bloqués.
- À l’enrôlement, Android propose d’exclure l’application de l’économie de batterie : il faut accepter. Sur certaines marques (itel, Tecno, Infinix, Xiaomi, Samsung) il faut aussi autoriser le démarrage automatique dans les réglages de la batterie.
- Le téléphone envoie un rapport de santé à chaque synchronisation (champ health_report).
| Android | Verrouillage et échéance hors ligne | Localisation rallumée à distance |
|---|---|---|
| 8.0 et 8.1 | Oui | Selon le modèle |
| 9 et 10 | Oui | Selon le modèle |
| 11 et plus | Oui | Oui |
Dépannage : mon téléphone ne s’est pas verrouillé
- 1. Ouvrez la fiche : regardez applied_state et command.state. Si command.state vaut PENDING ou FETCHED, le téléphone n’a pas encore confirmé l’ordre.
- 2. Regardez diagnosis : chaque code dit quoi faire (voir « Diagnostic : codes et que faire »).
- 3. Demandez au client d’ouvrir l’application de sécurité : elle se synchronise et se verrouille aussitôt si le paiement est en retard.
- 4. Vérifiez la batterie : l’application doit être « Sans restriction ».
- 5. Pour les prochains contrats, utilisez l’échéance exacte (paid_duration et lock_at_valid_until) : le téléphone se verrouille seul à l’heure, même hors ligne.
Erreurs
Toute erreur a la forme { "statusCode", "code", "message", "params" }.
| Statut | Code | Sens |
|---|---|---|
| 400 | VALIDATION_* | Champ invalide (le code précise lequel). |
| 400 | VALIDATION_FRP_ACCOUNTS | Identifiants de comptes Google invalides (5 à 30 chiffres, cinq au plus). |
| 401 | API_KEY_INVALID, API_SIGNATURE_INVALID, API_TIMESTAMP_INVALID | Authentification refusée. |
| 402 | INSUFFICIENT_BALANCE | Solde insuffisant : rien n'est enregistré. |
| 403 | ACCOUNT_NOT_VERIFIED, API_SCOPE_DENIED, API_IP_NOT_ALLOWED | Compte non vérifié, droit manquant, adresse non autorisée. |
| 403 | API_ACCESS_NOT_ENABLED | Compte entreprise : l'accès à l'API n'a pas encore été accordé (à demander dans le portail, menu « Accès aux API »). |
| 404 | DEVICE_NOT_FOUND, CONTRACT_NOT_FOUND | Introuvable (ou appareil d'un autre vendeur). |
| 409 | DEVICE_QUOTA_REACHED | Plafond d'appareils atteint : refus de fond, inutile de réessayer tant que le plafond n'est pas relevé (demande dans le portail). |
| 409 | API_REPLAY_DETECTED | Cette signature a déjà été reçue : renvoyez avec un nouvel horodatage, donc une nouvelle signature. |
| 409 | DEVICE_OR_CONTRACT_TAKEN | IMEI, numéro de série ou contrat déjà utilisé. |
| 409 | DEVICE_REPORTED_LOST | Déverrouillage refusé : l'appareil est déclaré perdu ou volé. |
| 409 | IDEMPOTENCY_IN_PROGRESS | Une requête avec la même Idempotency-Key est encore en cours : réessayez dans quelques secondes. |
| 422 | IDEMPOTENCY_KEY_REUSED | Même Idempotency-Key avec un contenu différent. |
| 429 | RATE_LIMITED | Trop de requêtes (120 par minute et par clé). |
| 422 | DEVICE_TYPE_NOT_AVAILABLE | Type d'appareil pas disponible pour le moment (Windows, macOS) : rien n'est enregistré ni débité. |