Le dossier est le produit : le Health ID d'OpesCare
Jude Nshome explique comment OpesCare fait du dossier médical du patient une infrastructure portable et régie par le consentement, au Cameroun.
Pas une application de transactions de santé, mais une application de relations de santé. Le dossier circule entre le patient, sa famille, la clinique et l'aidant — et chaque passage de relais est consenti, tracé et révocable.
Dans la plupart des systèmes de santé d'Afrique subsaharienne — et, pour être franc, dans la plupart des systèmes de santé du monde —, le dossier médical appartient à l'établissement qui l'a créé. Les résultats d'analyse dorment dans un classeur d'hôpital. L'historique des ordonnances reste dans une seule pharmacie. Quand un patient déménage, part à l'étranger, consulte ailleurs ou perd son carnet, son dossier ne le suit pas. Les réglages par défaut sont à l'envers : l'établissement est durable, le patient est une source de friction.
OpesCare renverse cette logique. C'est une plateforme de santé bilingue (français et anglais) pour le Cameroun, dans laquelle le dossier du patient — identité, antécédents, médicaments, groupe sanguin, allergies, résultats d'analyses, réglages de consentement et historique des accès — est le produit lui-même. Autour de ce dossier s'organise une couche de marché pour trouver des soins dans le monde réel : les stocks de médicaments déclarés par les pharmacies proches, la disponibilité du sang, les cliniciens et l'annuaire national des structures de santé. Et la même application, sur le même téléphone, sert aussi de poste de travail à la clinique : médecin, accueil, pharmacien et technicien de laboratoire ont chacun leur espace, tous travaillant sur le dossier qui appartient au patient.
- Le dossier du patient Identité, antécédents, médicaments, allergies, résultats, réglages de consentement et historique des accès. Tout le reste s'appuie sur ce dossier unique.
- Couche marché Trouve les soins dans le monde réel : stocks déclarés par les pharmacies, disponibilité du sang et cliniciens sur une carte.
- Couche gouvernance Décide qui peut lire le dossier : demandes et autorisations de consentement, journal des accès visible par le patient et voie d'urgence auditée.
- Couche famille Permet à un tuteur d'agir pour un enfant ou une personne à charge dans un contexte « agir pour » explicite ; un adulte ne rejoint qu'en acceptant une invitation.
- Poste de la clinique La même application donne au médecin, à l'accueil, au pharmacien et au technicien de laboratoire leurs propres outils sur le même dossier.
Comment ce diagramme est vérifié 21 contrôles sur 21 réussis
L'image, les étapes, le PDF et le JSON sont générés à partir d'une seule spécification. Un test automatisé démontre le graphe et vérifie chaque référence ci-dessous dans l'application en fonctionnement.
- Chaque carte a un identifiant unique
- Chaque carte a un type connu
- Chaque flèche relie deux cartes existantes
- Un seul point de départ (personne, appareil ou partenaire)
- Chaque carte est atteignable depuis le départ
- Chaque carte mène à une issue (aucune impasse)
- Aucun cycle sauf les boucles de nouvel essai déclarées
- Chaque étape numérotée suit de vraies flèches
- Les issues terminent le flux
- Deux cartes ne partagent jamais une case
- Chaque carte cite sa preuve
- Chaque route, classe, méthode et réglage existe
- Chaque icône vient du jeu Lucide
- Chaque icône correspond au type de sa carte
- Chaque libellé existe en anglais et en français
- Chaque libellé tient dans sa carte
- Aucune flèche ne traverse une carte
- Deux flèches sans rapport ne se superposent jamais
- Aucune étiquette de flèche ne couvre une carte ou une autre étiquette
- Chaque pointe de flèche est visible
- Deux cartes reliées de types différents ne partagent jamais une couleur
| Carte | Preuve dans le code |
|---|---|
| Patient | route:portals.patient.access-logs class:App\Modules\PatientIdentity\Services\PatientIdentityService |
| Médicaments | route:public.network.medicine-finder |
| Sang | route:public.network.blood-finder |
| Soins | route:public.care-map |
| Invitations | route:portals.patient.family.invite.send |
| Pharmacie et labo | config:staff_capabilities.capabilities.dispense config:staff_capabilities.capabilities.lab_results |
| Accueil | config:staff_capabilities.capabilities.front_desk |
| Médecin | config:staff_capabilities.capabilities.clinical_chart |
| Tuteur | route:portals.patient.family |
| Bris de glace | method:App\Modules\Governance\Services\EmergencyAccessService::openBreakGlass config:staff_capabilities.capabilities.emergency_access |
| Contexte « agir pour » | method:App\Http\Middleware\ActAsDependent::handle |
| Consentements | class:App\Models\ConsentGrant method:App\Support\ClinicalRelationshipGate::evaluate |
| Le dossier du patient | class:App\Models\Patient class:App\Modules\PatientIdentity\Services\PatientIdentityService |
| Journal des accès | class:App\Models\AccessLog route:portals.patient.access-logs |
| Refusé | method:App\Support\ClinicalRelationshipGate::none |
| Lecture limitée | uri:GET api/mobile/access-logs |
Cet article en fait le tour, fonction par fonction : ce qu'un patient peut faire dès le premier jour, ce que peut faire le personnel derrière le comptoir, et la mécanique de gouvernance qui rend l'ensemble digne de confiance pour héberger des données médicales.
1. La colonne vertébrale : un Health ID portable
Chaque patient sur OpesCare reçoit le premier Health ID du Cameroun — une attestation vérifiable, présentée comme le document qu'elle est, avec un QR code et un lien de vérification qu'un clinicien peut scanner au moment du soin. Elle se prolonge en Passeport santé : une présentation longue et vivante du dossier (identité et synthèse médicale, médicaments, allergies et alertes, consultations, actes, vaccinations, santé maternelle et infantile), mise en page comme un passeport plutôt que comme un tableau de bord administratif, car c'est le format auquel les populations rurales et à faibles ressources accordent encore spontanément leur confiance : un document que l'on tient en main. Une page sans données reste vide ; rien n'est inventé pour la remplir.
Deux choix de conception comptent plus qu'il n'y paraît.
- L'identifiant fonctionne d'un établissement à l'autre. Le patient présente sa carte dans une nouvelle clinique, qui peut la vérifier et demander l'accès. Aucun patient n'est lié à « sa » clinique.
- La carte est conçue pour être montrée sans risque. Elle dispose d'un mode présentation ; les écrans qui affichent des données de santé bloquent les captures et l'enregistrement d'écran ; et l'application peut être verrouillée par la biométrie du téléphone, de sorte qu'un appareil laissé à un point de recharge ne s'ouvre pas directement sur le statut VIH ou l'historique de grossesse de quelqu'un.
- Présenter le QR Health ID Le patient ouvre sa carte Health ID en mode présentation.
- Scan, /verify/qr/{token} N’importe quelle caméra ouvre la page de vérification sur opescare.com.
- Vérifier l’état du jeton Le jeton est recherché et contrôlé : expiration et révocation.
- Vérifié, expiré, révoqué ou invalide La page affiche exactement l’un des quatre états.
- Enregistrer le scan réussi Une vérification réussie est enregistrée comme événement de vérification.
Comment ce diagramme est vérifié 21 contrôles sur 21 réussis
L'image, les étapes, le PDF et le JSON sont générés à partir d'une seule spécification. Un test automatisé démontre le graphe et vérifie chaque référence ci-dessous dans l'application en fonctionnement.
- Chaque carte a un identifiant unique
- Chaque carte a un type connu
- Chaque flèche relie deux cartes existantes
- Un seul point de départ (personne, appareil ou partenaire)
- Chaque carte est atteignable depuis le départ
- Chaque carte mène à une issue (aucune impasse)
- Aucun cycle sauf les boucles de nouvel essai déclarées
- Chaque étape numérotée suit de vraies flèches
- Les issues terminent le flux
- Deux cartes ne partagent jamais une case
- Chaque carte cite sa preuve
- Chaque route, classe, méthode et réglage existe
- Chaque icône vient du jeu Lucide
- Chaque icône correspond au type de sa carte
- Chaque libellé existe en anglais et en français
- Chaque libellé tient dans sa carte
- Aucune flèche ne traverse une carte
- Deux flèches sans rapport ne se superposent jamais
- Aucune étiquette de flèche ne couvre une carte ou une autre étiquette
- Chaque pointe de flèche est visible
- Deux cartes reliées de types différents ne partagent jamais une couleur
| Carte | Preuve dans le code |
|---|---|
| Présenter le QR Health ID | route:portals.patient.health-id-card method:App\Services\Identity\QrTokenService::generateToken |
| Scan, /verify/qr/{token} | route:verify.qr |
| Vérifier l’état du jeton | method:App\Http\Controllers\MedicalId\VerifyController::qr method:App\Services\Identity\QrTokenService::verifyToken method:App\Services\Identity\QrTokenService::failureState |
| Enregistrer le scan réussi | class:App\Models\HealthIdVerificationEvent |
| Invalide | method:App\Services\Identity\QrTokenService::failureState |
| Révoqué | method:App\Services\Identity\QrTokenService::revokeToken |
| Expiré | class:App\Models\HealthIdQrToken |
| Vérifié | route:verify.qr |
Le sceptique posera la question évidente, et c'est la bonne : au Cameroun, le téléphone est souvent partagé, et le meilleur chiffrement ne vaut rien si la personne qui ramasse l'appareil hérite de tout ce que voyait la session de son propriétaire. OpesCare fait de cette réalité une contrainte de conception, pas un cas marginal. Les jetons sont conservés dans le stockage sécurisé du système ; la déconnexion ou l'expiration de la session efface tout — requêtes en cache, cache hors ligne, file d'attente des envois et PDF exportés — et la protection de la confidentialité s'applique à chaque écran qui affiche des données du patient.
2. La couche de marché : trouver les soins là où ils sont
Un annuaire qui se contente de lister des établissements n'est qu'un bottin. OpesCare traite l'offre de santé comme un inventaire qui existe maintenant, et l'expose à travers trois services.
Recherche de médicaments. Le patient cherche un médicament, voit les stocks déclarés par les pharmacies du réseau, classés par distance, et peut réserver une quantité dans la pharmacie qui l'a réellement. La réservation arrive dans la file de travail de cette pharmacie, où le pharmacien la confirme et la délivre. La disponibilité provient uniquement de relevés de stock en direct dont la source figure sur une liste autorisée : si aucune pharmacie n'a déclaré le médicament, la recherche le dit, au lieu d'afficher des données de remplissage.
Recherche de sang. Le sang est le stock le plus périssable qu'un système de santé ait à gérer. OpesCare affiche la disponibilité par groupe sanguin et par produit sur une carte, prend en charge les demandes urgentes et suit chaque demande jusqu'à son issue. Puis il fait ce qu'un outil d'approvisionnement fait rarement : il invite le patient à devenir donneur, avec un annuaire de donneurs et des contenus de sensibilisation — car augmenter le nombre de donneurs est la seule stratégie qui ait jamais résolu une pénurie de sang à grande échelle.
Recherche de soins. Cliniciens, établissements et annuaire national partagent une même interface cartographique : on trouve un clinicien disponible et proche, on réserve le créneau et on s'y rend. La carte n'est pas un ornement ; elle montre l'offre de soins en réseau d'un pays selon un autre axe que les rendez-vous — la géographie plutôt que l'agenda.
C'est un marché parce que cela ne fonctionne que si chaque côté de la transaction est instrumenté, et pas seulement la barre de recherche. Les annuaires échouent précisément quand le stock, l'urgence et la confiance ne coïncident pas.
3. La couche de gouvernance : le consentement comme protocole à part entière
C'est ici qu'OpesCare cesse de ressembler à une application bien faite pour ressembler à une infrastructure de santé.
Un clinicien qui veut consulter un dossier qui ne lui a pas été ouvert n'obtient pas de contournement : il fait une demande. Le téléphone du patient l'affiche sur une fiche de consentement — qui demande, pourquoi, quelles données et pour combien de temps — et le patient accepte ou refuse. Les autorisations sont conservées dans un registre des consentements. Chaque accès au dossier est inscrit dans un journal des accès consultable par le patient : la personne à qui appartiennent les données voit qui les a ouvertes, et quand. Le consentement peut être révoqué, ce qui fait du « consentement » un mot plutôt qu'une case à cocher. Enfin, la demande de rectification reconnaît que les dossiers contiennent des erreurs : le patient dépose sa demande depuis l'application et elle est examinée par le service des droits des patients d'OpesCare.
- Recherche du Health ID Le clinicien scanne le QR du patient ou saisit son Health ID.
- Identité seule, pas de dossier Sans rendez-vous, lien d’équipe de soins ou accord, le dossier reste fermé.
- Demande : portée + motif La demande précise ce qui sera lu et pourquoi.
- Notification au patient La demande apparaît dans l’application du patient avec une notification.
- Approuver (ou refuser) Le patient décide. Un refus met fin au flux.
- Accord : portée, expires_at Un accord de consentement est enregistré avec sa portée et sa date d’expiration.
- Ouvrir le dossier Le clinicien consulte le dossier.
- Contrôle du fondement Le contrôle de relation clinique n’autorise que ce que couvre l’accord.
- Accès journalisé Qui a lu quoi, quand et sur quel fondement est inscrit au journal visible par le patient.
- Données limitées renvoyées Seule la partie autorisée du dossier est renvoyée.
- Révocation ou expiration Le patient peut révoquer à tout moment ; sinon l’accord expire à son échéance.
- Demande refusée Si le patient refuse la demande, aucun accord n'est créé et le dossier reste fermé.
Comment ce diagramme est vérifié 21 contrôles sur 21 réussis
L'image, les étapes, le PDF et le JSON sont générés à partir d'une seule spécification. Un test automatisé démontre le graphe et vérifie chaque référence ci-dessous dans l'application en fonctionnement.
- Chaque carte a un identifiant unique
- Chaque carte a un type connu
- Chaque flèche relie deux cartes existantes
- Un seul point de départ (personne, appareil ou partenaire)
- Chaque carte est atteignable depuis le départ
- Chaque carte mène à une issue (aucune impasse)
- Aucun cycle sauf les boucles de nouvel essai déclarées
- Chaque étape numérotée suit de vraies flèches
- Les issues terminent le flux
- Deux cartes ne partagent jamais une case
- Chaque carte cite sa preuve
- Chaque route, classe, méthode et réglage existe
- Chaque icône vient du jeu Lucide
- Chaque icône correspond au type de sa carte
- Chaque libellé existe en anglais et en français
- Chaque libellé tient dans sa carte
- Aucune flèche ne traverse une carte
- Deux flèches sans rapport ne se superposent jamais
- Aucune étiquette de flèche ne couvre une carte ou une autre étiquette
- Chaque pointe de flèche est visible
- Deux cartes reliées de types différents ne partagent jamais une couleur
| Carte | Preuve dans le code |
|---|---|
| Clinicien | config:staff_capabilities.capabilities.identify_patient route:portals.chart.consent_request |
| Recherche du Health ID | uri:POST api/mobile/doctor/patients/scan method:App\Http\Controllers\Api\Mobile\MobileProviderScanController::scan |
| Base pour lire ? | method:App\Support\ClinicalRelationshipGate::evaluate method:App\Support\ClinicalRelationshipGate::holds |
| Demande : portée + motif | uri:POST api/mobile/doctor/consent-requests method:App\Http\Controllers\Api\Mobile\MobileConsentRequestController::store config:staff_capabilities.capabilities.request_access |
| Données limitées renvoyées | uri:GET api/mobile/doctor/patients/{patient} method:App\Http\Controllers\Api\Mobile\MobileDoctorChartController::show |
| Accès journalisé | class:App\Models\AccessLog uri:GET api/mobile/access-logs |
| Contrôle du fondement | method:App\Support\ClinicalRelationshipGate::hasActiveConsentGrant method:App\Support\ClinicalRelationshipGate::coversFullRecord |
| Notification au patient | uri:GET api/mobile/consent-requests uri:GET api/mobile/consent-requests/pending-count |
| Lectures suivantes refusées | uri:POST api/mobile/consents/{id}/revoke method:App\Http\Controllers\Api\Mobile\MobileGovernanceController::revokeConsent |
| Accord : portée, expires_at | class:App\Models\ConsentGrant method:App\Http\Controllers\Api\Mobile\MobileGovernanceController::approveConsent |
| Approuver (ou refuser) | uri:POST api/mobile/consent-requests/{id}/approve uri:POST api/mobile/consent-requests/{id}/deny |
| Demande refusée | method:App\Http\Controllers\Api\Mobile\MobileGovernanceController::denyConsent |
Il existe aussi une voie d'urgence. L'accès « bris de glace », pour un patient inconscient qui ne peut pas consentir, exige un motif, n'ouvre qu'un profil d'urgence limité, alerte le patient et fait l'objet d'un contrôle a posteriori — car un système de gouvernance sans accès d'urgence est un système qui coûte des vies.
À l'export, le dossier a deux portes : le PDF pour les personnes et FHIR R4 pour les systèmes, de sorte qu'un autre prestataire reçoit un format normalisé plutôt qu'une capture d'écran. Les dossiers peuvent aussi être importés — un lot FHIR ou des documents numérisés — avec une honnêteté rare dans les applications de santé : les données importées sont conservées et étiquetées comme fournies par le patient et non vérifiées. La provenance compte trop pour être maquillée.
Le même esprit traverse tout le produit : la piste d'audit est tournée vers le patient, et non vers un journal serveur que personne ne lit. Dans la plupart des dossiers informatisés, « qui a consulté ce dossier » relève d'une base de conformité. Dans OpesCare, c'est un écran que le patient peut interroger. Quand la gouvernance est lisible par ceux qu'elle gouverne, elle cesse d'être du théâtre.
4. La couche familiale : agir pour ses proches
Une grand-mère qui gère son diabète n'a ni les mêmes compétences numériques, ni le même niveau de lecture, ni le même téléphone que ses petits-enfants. Derrière beaucoup de dossiers se cache une réalité familiale : un aidant qui manipule le téléphone, ou un proche qui arrive en pleine urgence alors que personne ne peut consentir.
OpesCare dispose d'une véritable architecture familiale, pas d'un simple bouton « partager mon dossier ». Un tuteur peut agir pour ses enfants et les personnes à sa charge dans un contexte « agir pour » explicite, et les écrans de la personne à charge indiquent à qui appartient le dossier ouvert, ce qui évite les confusions d'identité qui mettent les patients en danger. Le dossier d'une personne à charge se lit par les mêmes points d'accès que celui du tuteur : un parent qui s'occupe d'un enfant dispose d'un vrai accès au dossier, et non d'un compte lié cosmétique dont on aurait retiré l'essentiel.
- Le tuteur se connecte Le tuteur utilise son propre compte ; aucun mot de passe n'est partagé.
- Contexte « agir pour » Choisir une personne à charge joint un contexte « agir pour » à chaque requête de l'application.
- Vérification du lien familial Le serveur vérifie que le tuteur détient bien un lien familial avec cette personne avant de répondre.
- Dossier de la personne à charge Le dossier se lit par les mêmes points d'accès que celui du tuteur, et l'écran indique à qui il appartient.
- Inviter un adulte Un adulte ne peut pas être ajouté comme personne à charge ; il est invité.
- En attente Tant que la personne invitée n'a pas répondu, l'invitation ne donne aucun accès.
- Acceptée Une fois acceptée, le lien est actif et peut encore être retiré plus tard.
- Refusée Une invitation refusée ne donne aucun droit.
Comment ce diagramme est vérifié 21 contrôles sur 21 réussis
L'image, les étapes, le PDF et le JSON sont générés à partir d'une seule spécification. Un test automatisé démontre le graphe et vérifie chaque référence ci-dessous dans l'application en fonctionnement.
- Chaque carte a un identifiant unique
- Chaque carte a un type connu
- Chaque flèche relie deux cartes existantes
- Un seul point de départ (personne, appareil ou partenaire)
- Chaque carte est atteignable depuis le départ
- Chaque carte mène à une issue (aucune impasse)
- Aucun cycle sauf les boucles de nouvel essai déclarées
- Chaque étape numérotée suit de vraies flèches
- Les issues terminent le flux
- Deux cartes ne partagent jamais une case
- Chaque carte cite sa preuve
- Chaque route, classe, méthode et réglage existe
- Chaque icône vient du jeu Lucide
- Chaque icône correspond au type de sa carte
- Chaque libellé existe en anglais et en français
- Chaque libellé tient dans sa carte
- Aucune flèche ne traverse une carte
- Deux flèches sans rapport ne se superposent jamais
- Aucune étiquette de flèche ne couvre une carte ou une autre étiquette
- Chaque pointe de flèche est visible
- Deux cartes reliées de types différents ne partagent jamais une couleur
| Carte | Preuve dans le code |
|---|---|
| Tuteur | route:portals.patient.family uri:GET api/mobile/family |
| « Agir pour » | route:portals.patient.family.switch class:App\Http\Middleware\ActAsDependent |
| Lien familial | method:App\Http\Middleware\ActAsDependent::handle class:App\Models\FamilyLink |
| Dossier du proche | uri:GET api/mobile/family/members/{linkId} |
| Refusé | method:App\Http\Middleware\ActAsDependent::handle |
| Dossier affiché | route:portals.patient.family.switch.back |
| Inviter un adulte | uri:POST api/mobile/family/invitations route:portals.patient.family.invite.send |
| En attente | uri:GET api/mobile/family/invitations/incoming route:portals.patient.family.invitations |
| Acceptée | uri:POST api/mobile/family/invitations/{id}/accept method:App\Http\Controllers\Api\Mobile\MobileFamilyController::acceptInvitation |
| Peut aider aux soins | class:App\Models\FamilyLink route:portals.patient.family.revoke |
| Refusée | uri:POST api/mobile/family/invitations/{id}/decline method:App\Http\Controllers\Api\Mobile\MobileFamilyController::declineInvitation |
La délégation elle-même est encadrée. Un adulte peut être invité à aider à gérer les soins d'une personne, et l'invitation peut être acceptée ou refusée — tant qu'elle n'est pas acceptée, elle ne donne aucun droit. Il n'y a pas de réseau social, et personne n'est ajouté aux soins de quelqu'un sans avoir dit oui.
Pour les équipes produit, c'est la couche à étudier. Beaucoup d'applications de santé sont conçues pour le titulaire du compte, et la grand-mère reste sans solution. OpesCare a été conçu pour le foyer dès le départ.
5. Une application, cinq postes de la clinique
Le choix de produit le moins visible d'OpesCare — et sans doute le plus audacieux — est que la même application sert toute la clinique.
Quand un membre du personnel se connecte, l'application prend une forme différente selon les capacités de son rôle, définies par le serveur. La journée d'un médecin, c'est un agenda, une boîte de demandes de rendez-vous, une liste de patients et un dossier découpé en sections. L'accueil dispose de l'agenda de l'établissement : enregistrement des arrivées, inscription des patients sans rendez-vous et orientation vers un clinicien. Le pharmacien reçoit les ordonnances, délivre, gère les réservations et ajuste les stocks avec une piste d'audit. Le technicien de laboratoire enregistre les prélèvements, saisit et valide les résultats — et la validation d'un résultat prévient à la fois le patient et le clinicien prescripteur.
- Patient Détient le Health ID, répond aux demandes de consentement et consulte le journal des accès.
- Médecin Agenda, demandes de rendez-vous, liste de patients, dossier par sections, prescription, demandes d'analyses, suivis et boîte de résultats.
- Accueil L'agenda de l'établissement : enregistrement des arrivées, inscription sans rendez-vous et orientation vers un clinicien.
- Pharmacien Réception des ordonnances, délivrance, réservations et ajustements de stock avec piste d'audit.
- Technicien de laboratoire Prélèvements, saisie et validation des résultats ; la validation prévient le patient et le clinicien prescripteur.
- Capacités et relation de soins Le serveur décide de ce que chaque rôle peut faire et vérifie une relation de soins ou un consentement avant toute lecture d'un dossier par un clinicien.
- Un seul dossier Les cinq travaillent sur le même dossier : rien n'a besoin de faire le tour de la clinique.
Comment ce diagramme est vérifié 21 contrôles sur 21 réussis
L'image, les étapes, le PDF et le JSON sont générés à partir d'une seule spécification. Un test automatisé démontre le graphe et vérifie chaque référence ci-dessous dans l'application en fonctionnement.
- Chaque carte a un identifiant unique
- Chaque carte a un type connu
- Chaque flèche relie deux cartes existantes
- Un seul point de départ (personne, appareil ou partenaire)
- Chaque carte est atteignable depuis le départ
- Chaque carte mène à une issue (aucune impasse)
- Aucun cycle sauf les boucles de nouvel essai déclarées
- Chaque étape numérotée suit de vraies flèches
- Les issues terminent le flux
- Deux cartes ne partagent jamais une case
- Chaque carte cite sa preuve
- Chaque route, classe, méthode et réglage existe
- Chaque icône vient du jeu Lucide
- Chaque icône correspond au type de sa carte
- Chaque libellé existe en anglais et en français
- Chaque libellé tient dans sa carte
- Aucune flèche ne traverse une carte
- Deux flèches sans rapport ne se superposent jamais
- Aucune étiquette de flèche ne couvre une carte ou une autre étiquette
- Chaque pointe de flèche est visible
- Deux cartes reliées de types différents ne partagent jamais une couleur
| Carte | Preuve dans le code |
|---|---|
| Une seule app OpesCare | route:login.submit config:staff_capabilities.capabilities |
| Patient | route:portals.patient.access-logs |
| Médecin | config:staff_capabilities.capabilities.clinical_chart config:staff_capabilities.capabilities.prescribe |
| Accueil | config:staff_capabilities.capabilities.front_desk |
| Pharmacien | config:staff_capabilities.capabilities.dispense |
| Laboratoire | config:staff_capabilities.capabilities.lab_results route:portals.lab.release |
| Contrôle des droits | config:staff_capabilities.capabilities method:App\Support\ClinicalRelationshipGate::evaluate |
| Refusé | method:App\Support\ClinicalRelationshipGate::none |
| Un seul dossier patient | class:App\Models\Patient class:App\Models\AccessLog |
| Lecture journalisée | uri:GET api/mobile/access-logs |
Pourquoi est-ce audacieux ? Parce que le dossier est un objet unique, partagé par ces cinq personnes. Quand la même application fonctionne à l'accueil, chez le médecin, à la pharmacie et au laboratoire, le dossier n'a pas à faire le tour de la clinique : il y est déjà. Les passages de relais qui se perdent sur papier (un bon qui ne suit pas le prélèvement, une ordonnance qui ne suit pas la consultation) deviennent la norme.
À ces rôles s'ajoutent les capacités cliniques qu'exige le dossier : prescription, demandes d'analyses, demandes de suivi, boîte de résultats, scan du QR code du Health ID au moment du soin, et la voie « bris de glace » décrite plus haut. L'application du clinicien est un véritable outil de rédaction du dossier — pas un tableau de facturation plaqué dessus.
6. Sous le capot (pour les ingénieurs)
Pour un public technique, l'architecture est propre et les choix d'ingénierie sont prudents là où il le faut.
- Expo SDK 57, React Native 0.86, React 19 et TypeScript strict : une seule base de code TypeScript pour l'application Android, avec un profil de compilation iOS préparé et une version web.
- Routage d'abord : un routage par fichiers sur plus de quatre-vingt-dix écrans, avec un audit de navigation dans le dépôt qui signale tout écran vers lequel rien ne mène.
- État serveur et état client séparés : TanStack Query et Zustand, avec un seul client HTTP et un seul endroit où les jetons sont joints et renouvelés.
- Un modèle de session explicite : un seul emplacement de jeton par appareil, avec un marqueur de type de session, pour qu'une session de clinicien et une session de patient ne puissent pas se contaminer.
- Hors ligne, avec un circuit de règlement : un cache local chiffré, activé sur demande et délimité par une politique par appareil émise et révocable par le serveur, et une file d'envoi qui capte les écritures échouées faute de réseau et les règle dans une file chiffrée côté serveur — dédoublonnée, auditée et ouverte comme tâche de rapprochement. Les requêtes d'authentification ne sont jamais écrites sur l'appareil.
- Sécurité du transport : sur Android, une configuration de sécurité réseau épingle TLS sur les clés des autorités de certification racines plutôt que sur le certificat du site, pour que les renouvellements de routine ne cassent pas les applications installées ; aucun trafic en clair hors d'un profil de développement local, sauvegardes de l'application désactivées, et une liste d'autorisations courte qui bloque explicitement le stockage, le micro et la superposition d'écran dont l'application n'a pas besoin.
- Contrôle qualité : TypeScript, ESLint et une suite Jest ciblant les chemins critiques pour la sécurité — effacement de session, stockage des jetons, renouvellement d'un jeton expiré une seule fois, verrouillage de l'application, ouverture sûre des liens externes et stockage hors ligne —, le tout exécuté en intégration continue à chaque envoi et à chaque demande de fusion.
- L'internationalisation est systémique, pas cosmétique : français et anglais à parité dans toute l'application, et montants en XAF (FCFA).
- Des paiements pensés pour le marché : le règlement d'une facture par MTN Mobile Money ou Orange Money en XAF est intégré — et la règle du produit est inscrite dans le code et ses tests : les soins ne sont jamais conditionnés au paiement.
- Politique hors ligne Le patient l'active. Le serveur émet pour cet appareil une politique qui précise les parties du dossier pouvant être conservées, et peut la révoquer.
- Cache chiffré Seuls les périmètres autorisés par la politique sont stockés sur le téléphone, chiffrés.
- Une modification hors ligne Une modification faite sans réseau n'atteint pas le serveur.
- File d'envoi L'écriture échouée est conservée dans une file d'envoi. Les requêtes de connexion ne sont jamais écrites sur l'appareil.
- Retour du réseau À la reconnexion, la file d'envoi est transmise au serveur.
- File chiffrée côté serveur Chaque modification est stockée chiffrée et dédoublonnée : l'envoyer deux fois ne l'applique pas deux fois.
- Tâche de rapprochement Chaque modification mise en file ouvre une tâche de rapprochement sur le serveur.
- Piste d'audit La création de la politique, chaque modification mise en file et la révocation sont inscrites dans la piste d'audit.
- Déconnexion ou retrait La déconnexion, ou la révocation de la politique, efface du téléphone le cache, la file d'envoi et la politique enregistrée.
Comment ce diagramme est vérifié 21 contrôles sur 21 réussis
L'image, les étapes, le PDF et le JSON sont générés à partir d'une seule spécification. Un test automatisé démontre le graphe et vérifie chaque référence ci-dessous dans l'application en fonctionnement.
- Chaque carte a un identifiant unique
- Chaque carte a un type connu
- Chaque flèche relie deux cartes existantes
- Un seul point de départ (personne, appareil ou partenaire)
- Chaque carte est atteignable depuis le départ
- Chaque carte mène à une issue (aucune impasse)
- Aucun cycle sauf les boucles de nouvel essai déclarées
- Chaque étape numérotée suit de vraies flèches
- Les issues terminent le flux
- Deux cartes ne partagent jamais une case
- Chaque carte cite sa preuve
- Chaque route, classe, méthode et réglage existe
- Chaque icône vient du jeu Lucide
- Chaque icône correspond au type de sa carte
- Chaque libellé existe en anglais et en français
- Chaque libellé tient dans sa carte
- Aucune flèche ne traverse une carte
- Deux flèches sans rapport ne se superposent jamais
- Aucune étiquette de flèche ne couvre une carte ou une autre étiquette
- Chaque pointe de flèche est visible
- Deux cartes reliées de types différents ne partagent jamais une couleur
| Carte | Preuve dans le code |
|---|---|
| Le patient accepte | uri:POST api/mobile/offline/policies |
| Politique hors ligne | method:App\Modules\Offline\Services\SyncService::createLocalCachePolicy class:App\Models\LocalCachePolicy |
| Déconnexion ou retrait | uri:POST api/mobile/offline/policies/{policy}/revoke method:App\Modules\Offline\Services\SyncService::revokeLocalCachePolicy |
| Une modification hors ligne | class:App\Models\OfflineQueue |
| Cache chiffré | class:App\Models\LocalCachePolicy |
| File d'envoi | class:App\Models\OfflineQueue |
| Retour du réseau | uri:POST api/mobile/offline/policies/{policy}/queue |
| File chiffrée côté serveur | method:App\Modules\Offline\Services\SyncService::queueEncryptedPayload class:App\Models\OfflineQueue |
| Rapprochement | class:App\Models\ReconciliationCase method:App\Modules\Offline\Services\SyncService::detectConflict |
| Modification appliquée | method:App\Modules\Offline\Services\SyncService::resolveConflict |
| Piste d'audit | class:App\Models\AuditEvent |
7. Les limites, en toute honnêteté
Une publication sérieuse ne prétend pas qu'un jeune produit est achevé. Voici ce que demandera un relecteur technique, et où en sont les choses.
- Le paiement mobile n'est pas encore disponible. Les parcours MTN Mobile Money et Orange Money sont construits, mais les vrais paiements exigent d'abord le raccordement des comptes marchands d'OpesCare. D'ici là, l'application indique que le paiement est indisponible — et aucun rendez-vous, dossier ou consultation n'en dépend.
- Les notifications push n'atteignent pas encore la version publiée. Le dispositif existe de bout en bout, et l'application dit honnêtement ce qu'une version donnée peut ou ne peut pas faire plutôt que de promettre des alertes qui n'arrivent pas, mais les identifiants push doivent être livrés avec une version avant que rappels et alertes de résultats ne parviennent aux utilisateurs.
- L'épinglage TLS ne concerne qu'Android. La version iOS est par ailleurs préparée ; sur iPhone, les connexions reposent sur la validation standard des certificats, et la publication sur l'App Store reste à venir.
- La version web est la petite sœur, par choix. Les navigateurs n'ont pas de coffre sécurisé du système, si bien que la version web refuse le cache hors ligne et garde les jetons en mémoire, adossés uniquement à un stockage limité à l'onglet.
Une limite citée dans les premières versions de ce texte ne s'applique plus : depuis le 19 octobre 2026, l'application dialogue avec le serveur de production d'opescare.com. Une version de production doit nommer explicitement son API, et la compilation refuse d'être publiée par erreur vers un serveur de démonstration.
Nous préférons que ces limites soient visibles et corrigées en vol plutôt que passées sous silence.
8. Pourquoi cela compte au-delà du Cameroun
Il y a un schéma qui mérite d'être nommé. La technologie de santé conçue dans les pays à hauts revenus optimise en général la transaction : prendre rendez-vous, payer une facture, écrire à un médecin. Ce qu'elle manque, c'est la relation en dessous : à qui sont les données, qui peut les voir, qui agit pour vous quand vous ne le pouvez pas, et ce qui se passe quand le réseau tombe au moment précis où vous avez besoin de soins.
OpesCare construit d'abord ces fondations — propriété, consentement, délégation, accès hors ligne, identité — puis y accroche les transactions. Cet ordre traduit un pari précis : sur les marchés de santé émergents, l'infrastructure de confiance est la ressource rare, et les parcours de réservation sont des commodités que chacun peut acheter ou refaire.
Le Cameroun est le terrain de la preuve, mais le modèle ne lui est pas propre. Quelle que soit l'étiquette choisie — infrastructure de santé numérique, identité médicale portable, dossier de santé fondé sur le consentement, réseau d'identifiants de santé détenus par les patients, inédit en son genre —, l'idée de fond est simple :
Le dossier appartient au patient. Tout le reste découle du protocole.
OpesCare est développé par OPES Health Systems pour les soins au Cameroun. Il est disponible en français et en anglais, pour les patients comme pour le personnel, sur Android et sur le web dès aujourd'hui, iOS étant prévu. Pour aller plus loin : le consentement expliqué aux patients, le Health ID, FHIR et l'index maître des patients, ou la documentation développeurs.
Article compagnon : La plateforme derrière l'application — comment la pharmacie, le laboratoire, la banque de sang et l'accueil travaillent sur le même dossier.
Questions fréquentes
Que signifie « le dossier est le produit » ?
OpesCare est construit autour du dossier longitudinal du patient plutôt qu'autour d'une transaction isolée comme une réservation ou un paiement. La recherche de soins, le consentement, l'accès familial et les outils de la clinique reposent tous sur ce dossier unique, que le patient emporte avec lui.
Le Health ID d'OpesCare est-il la carte nationale d'identité ?
Non. C'est un identifiant de santé délivré par OpesCare. Il s'agit d'un réseau d'identifiants de santé détenus par les patients, inédit au Cameroun, mais ce n'est pas une pièce d'identité officielle et il ne revendique aucune approbation de l'État.
Une clinique peut-elle lire mon dossier simplement parce qu'elle a scanné mon Health ID ?
Non. Le scan vérifie votre identité. Pour lire votre dossier, un clinicien doit avoir une relation de soins avec vous — un rendez-vous, une place dans votre équipe soignante ou un épisode ouvert dans sa structure — ou une autorisation que vous avez approuvée. Chaque lecture est inscrite dans un journal que vous pouvez consulter.
Que se passe-t-il en urgence si je ne peux pas consentir ?
Un clinicien peut recourir à l'accès « bris de glace ». Il doit indiquer un motif ; seul un profil d'urgence limité s'ouvre ; vous êtes alerté et l'accès est contrôlé ensuite.
Puis-je gérer le dossier de mon enfant ou de mon parent ?
Oui. Un tuteur peut agir pour un enfant ou une personne à charge dans un contexte « agir pour » explicite. Un adulte n'est jamais ajouté à son insu : il reçoit une invitation qu'il peut accepter ou refuser.
OpesCare fonctionne-t-il sans connexion ?
En partie, et seulement si vous l'activez. L'accès hors ligne conserve une copie chiffrée des parties de votre dossier qu'autorise une politique émise et révocable par le serveur. Les modifications faites hors ligne sont mises en attente et réglées avec le serveur à la reconnexion.
Dois-je payer pour être soigné via OpesCare ?
Non. Les soins ne sont jamais conditionnés au paiement. Le règlement d'une facture par MTN Mobile Money ou Orange Money est intégré à l'application, mais reste indisponible tant que les comptes marchands ne sont pas raccordés.
OpesCare est-il disponible sur iPhone ?
Android passe en premier. Une version iOS est préparée et la version web fonctionne dans n'importe quel navigateur, mais la publication sur l'App Store reste à venir.
Sources
- Flux de consentement — documentation OpesCare — OpesCare (consulté le 19 octobre 2026)
- Vérification par QR — documentation OpesCare — OpesCare (consulté le 19 octobre 2026)
- Ingestion FHIR R4 — documentation OpesCare — OpesCare (consulté le 19 octobre 2026)
- Architecture de la plateforme — documentation OpesCare — OpesCare (consulté le 19 octobre 2026)
- HL7 FHIR Release 4 — HL7 International (consulté le 19 octobre 2026)
- Loi n° 2010/012 du 21 décembre 2010 relative à la cybersécurité et à la cybercriminalité au Cameroun — Digital Watch Observatory (consulté le 19 octobre 2026)
- Configuration de la sécurité réseau (épinglage de certificats) — Android Developers (consulté le 19 octobre 2026)
Partager cet article
Trouver des soins avec OpesCare
Gratuit, en français et en anglais, partout au Cameroun.
Articles liés
Votre dossier de santé : consentement et accès
Guide du consentement sur OpesCare : qui peut ouvrir votre dossier, comment accepter ou révoquer un accès, l'accès d'urgence et votre journal des accès.
Health ID, interopérabilité et index patients : explications
Fonctionnement du Health ID OpesCare, de l'accès par consentement, de l'ingestion FHIR R4 et de l'index maître des patients, avec les outils de recherche.
Doublons de dossiers patients : le danger, et la réponse du MPI
Les doublons de dossiers scindent l'historique médical et causent des erreurs. Comment l'index maître d'OpesCare les repère et ne fusionne qu'après examen.
Cet article est une information générale, pas un avis médical. En cas d'urgence, rendez-vous à l'hôpital le plus proche.
Aucun commentaire
Soyez le premier à commenter. Les commentaires sont relus avant publication.
Laisser un commentaire
Tous les commentaires sont modérés.