A patient clicks a button labeled “Disconnect My Data.” The web app shows a green checkmark and a cheerful confirmation. Somewhere in a background queue, a worker process wakes up for its nightly sync, pulls a job created yesterday, and begins streaming two years of medication history to a downstream analytics cluster. The user trusted the interface. The system betrayed that trust.

This specific failure mode haunts health data architecture because the stakes are so high. A stale permission is not a minor bug; it is an active breach. The fix is to bind every single data request to a consent receipt: a small, structured record that carries the user’s intent from the UI all the way into your policy engine, your database transactions, and every background worker. It never stores clinical values. It only stores the right to access them, stamped with a version that cannot quietly mutate behind the scenes.

What the Receipt Actually Carries

Think of the receipt as a scoped contract, not a session flag. It contains a grant identifier, the data subject, the exact scope of access (lab results, vitals, medication history), a time-bound validity window, and a version number. When a frontend requests access on behalf of a user, the API issues this receipt. The frontend holds it. Every downstream service that wants to read health data must present the receipt to a central policy layer and receive an explicit nod before opening the record.

This matters because health systems often mistake a user account token for consent. A token says who you are. A receipt says what you are allowed to do right now. If the two drift apart, the receipt should always win.

Version Everything

Build your consent store as an append-only log. When a user first grants access to their immunization records, that is version one. If they later narrow the scope to exclude specific providers, or if they revoke entirely, do not overwrite the first entry. Write version two. The receipt in the worker’s hand still says version one, and the policy engine can see exactly what version one permitted and that it was superseded at a specific timestamp.

This immutability is your audit backbone. Six months later, when a compliance officer asks why a particular ETL job ran on a Thursday afternoon, you can trace the exact grant version the job carried and prove it was valid when the job started. If you store consent as a single boolean flag in a user profile, you erase that history. You lose the ability to distinguish between “this was never allowed” and “this was allowed when the job began but the user changed their mind two hours later.”

Design Endpoints for Honesty

A consent API should expose clear, specific routes. Let POST /grants create a new permission. Let GET /grants/{id} return the current state of a specific receipt. Let POST /grants/{id}/revoke initiate a revocation. Do not pretend that hitting revoke instantly erases every copy of health data floating in your pipelines. Instead, return a revocation operation ID with a 202 Accepted status. This tells the user the request is real, it is starting, and they can track it.

That operation ID becomes critical when the user double-clicks because the interface felt slow. If they submit a second revocation request, return the original operation ID. Idempotency here is not a nice-to-have; it prevents duplicate panic and gives the user a single source of truth for the status of their request.

Ask Permission at the Last Possible Moment

A common mistake is to check consent at the API gateway and then trust a cached flag deep inside a worker. Do not do this. The worker should carry its receipt through the job lifecycle. Right before it executes the query against the health record store, it must ask the policy layer: “Is version three of this specific grant still valid for this exact scope?” If the answer is no, the worker stops. It fails the job. It does not retry.

Retry logic is poison here. A version mismatch is not a network blip. It is a human decision. The user revoked, or the grant expired, or the scope shrank. If you retry three times and succeed on the fourth because of a race condition, you have just violated consent. Treat the mismatch as a hard failure, surface it to your dead-letter queue or operations dashboard, and let a human investigate.

Handle the Messy Scenarios

Les systèmes réels ne progressent pas par étapes bien ordonnées. Les utilisateurs laissent des onglets de navigateur ouverts. Les imports groupés durent vingt minutes. Les périmètres changent alors qu'une synchronisation est à mi-chemin. Votre API de consentement nécessite des règles explicites pour ces moments.

Onglets de navigateur obsolètes. Un utilisateur révoque l'accès dans un onglet fraîchement ouvert. Un onglet plus ancien, contenant toujours un objet d'autorisation provenant d'un chargement de page précédent, tente de se reconnecter. Votre backend doit rejeter immédiatement ce reçu obsolète et imposer une nouvelle révision du consentement. Une autorisation révoquée doit se comporter comme un passeport annulé : elle ne reprend pas vie simplement parce que le détenteur a retrouvé une vieille copie dans un tiroir.

Imports en cours. Si un import groupé est en cours et que l'utilisateur révoque l'accès, deux choses doivent se produire simultanément. Premièrement, cessez d'autoriser de nouvelles écritures dès l'instant où la version est rejetée. Deuxièmement, montrez à l'utilisateur l'avancement réel du nettoyage via l'ID d'opération. Donnez-lui une page de statut honnête : « Révocation acceptée. Dix-huit écritures en attente sont en cours de purge. » Ne laissez pas les workers valider des données en utilisant une version d'autorisation qui a déjà été marquée comme invalide.

Changements de périmètre. Supposons qu'un utilisateur ait initialement accordé l'accès à cinq ans d'historique et qu'il le réduise ultérieurement à six mois. Ne modifiez pas l'autorisation originale. Fermez la version un, émettez la version deux avec la fenêtre plus restreinte, et forcez tous les processus en cours à se réconcilier avec la nouvelle limite. L'ancienne version reste dans votre journal en tant que fait historique, et non en tant que permission active.

Règles de sécurité strictes

Les reçus eux-mêmes sont sensibles, mais ce ne sont pas des données cliniques. Gardez-les séparés dans votre architecture. Seul le patient ou un rôle spécifiquement délégué — tel qu'un tuteur légal ou un soignant autorisé — devrait pouvoir consulter ou révoquer un reçu. Appliquez cela au niveau de la couche de données, et pas seulement dans la table de routage de l'interface utilisateur.

Vos journaux d'erreurs tenteront d'aspirer des données de santé lorsque des tâches échouent. Combattez cette tendance de manière agressive. Lorsqu'un worker s'arrête parce qu'il a présenté un reçu de consentement invalide, enregistrez l'ID de l'autorisation, la version et l'erreur. Ne journalisez jamais l'identifiant du patient, le code de diagnostic ou la valeur de laboratoire que le worker tentait de récupérer. Les données de santé dans les journaux se propagent comme de la moisissure : elles sont sauvegardées, indexées et oubliées de manière à contourner vos contrôles d'accès habituels.

Enfin, ne restaurez jamais une ancienne autorisation active lors d'une récupération du système. Si vous effectuez un retour en arrière sur une base de données ou restaurez un instantané qui contient par hasard une version pré-révocation d'une table d'autorisations, votre runbook doit automatiquement désactiver ces permissions ressuscitées avant que le service n'accepte de nouveau du trafic. Les états de consentement historiques appartiennent au journal d'audit, jamais à l'ensemble des règles actives.