# Surveillance à distance des patients avec FHIR

> Des lectures de tension artérielle à domicile aux alertes : Observations de périphériques, webhooks signés, une règle idempotente et ce qui reste en dehors de la couche de données. Code testé.

Source: https://developers.anpheros.com/fr/guides/remote-patient-monitoring-fhir

**La surveillance à distance des patients (RPM) collecte les mesures qu'un patient prend à domicile — tension artérielle, poids, glycémie, saturation en oxygène — et permet à une équipe de soins de les visualiser et d'agir en conséquence entre les visites.** La partie données comprend quatre étapes : intégrer chaque lecture dans le dossier du patient en tant qu'Observation codée et attribuée au périphérique ; réagir à l'arrivée d'une nouvelle lecture ; décider avec une règle claire si elle nécessite une attention ; et conserver un enregistrement de ce que l'équipe a fait. Cette page construit ce pipeline sur HL7 FHIR R4 avec les webhooks Anpheros et le SDK TypeScript, avec du code qui a été exécuté sur la plateforme, et liste ce qui reste en dehors de la couche de données.

## Le pipeline

```
 cuff, scale, glucose meter, pulse oximeter
        │ (Bluetooth / vendor app / Apple Health / Health Connect)
        ▼
 patient's phone app ──► your backend ──► FHIR Observation (author: device) in the patient's record
                                                     │ webhook: resource.created (signed)
                                                     ▼
                                     your monitoring service: rule → alert → care team
                                                     │
                                     note, call, CarePlan change, next appointment
```

Trois choix déterminent si cela fonctionne en pratique :

- **Une lecture, une Observation.** Chaque mesure du brassard est une Observation avec LOINC 85354-9 et deux composantes (8480-6 systolique, 8462-4 diastolique), l'heure de la mesure et `author_type: device`. Contrairement aux résumés des dispositifs portables quotidiens, les lectures RPM sont des mesures individuelles qu'un clinicien peut examiner une par une.
- **Réagir aux événements, pas à un minuteur.** Un webhook par nouvelle Observation permet à la règle de s'exécuter quelques secondes après l'arrivée de la lecture.
- **Règles sur plusieurs lectures.** Une seule lecture élevée est souvent du bruit ; la plupart des protocoles examinent une série. La règle ci-dessous demande trois lectures consécutives à ou au-dessus de 140/90. Ce seuil est un exemple, pas un avis clinique : l'équipe de soins définit la règle pour chaque patient.

## Écriture des lectures depuis l'appareil

L'application mobile (ou le cloud du fournisseur de l'appareil, via votre backend) écrit chaque lecture :

```ts
await anpheros.observations.create(patientId, {
  code: '85354-9', category: 'vital-signs', effective_at: '2026-10-13T07:15:00-04:00', author_type: 'device',
  components: [{ code: '8480-6', value: 152, unit: 'mm[Hg]' }, { code: '8462-4', value: 95, unit: 'mm[Hg]' }],
}, { source_system: 'bp-cuff-app' });
```

Si les lectures proviennent d'Apple Health ou de Health Connect plutôt que directement de l'appareil, lisez-les sur le téléphone comme décrit dans [Données portables en FHIR](https://developers.anpheros.com/fr/guides/wearable-data-fhir), mais conservez les lectures individuelles de tension artérielle comme des Observations individuelles au lieu de moyennes quotidiennes.

## Réagir aux nouvelles lectures

Enregistrez un point de terminaison de webhook une fois (`POST /v1/webhooks` avec `events: ["resource.created"]`) ; la réponse contient le secret de signature. Ensuite, exécutez un petit service qui vérifie chaque livraison et évalue la règle :

```ts
import { createServer } from 'node:http';
import { Anpheros, apiKey, verifyWebhookSignature } from '@anpheros/sdk';

const anpheros = new Anpheros({ auth: apiKey(process.env.ANPHEROS_KEY!) });
const SECRET = process.env.WEBHOOK_SECRET!;
const seen = new Set<string>();                       // deliveries are at-least-once: de-duplicate by event id
const alerted = new Set<string>();                    // one alert per newest reading, however many events arrive

// Rule: the three most recent home readings all at or above 140/90 → tell the care team once
async function checkBloodPressure(patientId: string, observationId: string) {
  const obs = await anpheros.observations.get(observationId);
  if (obs.code !== '85354-9') return;                  // only blood pressure readings trigger the rule
  const { data } = await anpheros.observations.list(patientId, { code: '85354-9', limit: 3 });   // newest first
  const value = (o: typeof obs, code: string) => o.components.find(c => c.code === code)?.value ?? 0;
  const allHigh = data.length === 3 && data.every(o => value(o, '8480-6') >= 140 || value(o, '8462-4') >= 90);
  if (!allHigh || alerted.has(data[0].id)) return;
  alerted.add(data[0].id);
  console.log(`ALERT patient ${patientId}: last 3 readings ≥ 140/90 →`,
    data.map(o => `${value(o, '8480-6')}/${value(o, '8462-4')}`).join(', '));   // in production: notify the care team
}

createServer(async (req, res) => {
  const chunks: Buffer[] = [];
  for await (const c of req) chunks.push(c as Buffer);
  const body = Buffer.concat(chunks).toString('utf8');
  const ok = await verifyWebhookSignature({ secret: SECRET, header: req.headers['anpheros-signature'] as string, body });
  if (!ok) { res.writeHead(400).end(); return; }       // not from Anpheros, or too old
  res.writeHead(204).end();                            // answer fast; do the work after
  const event = JSON.parse(body);
  if (seen.has(event.id)) return;
  seen.add(event.id);
  const d = event.data;
  if (event.type === 'resource.created' && d.resource_type === 'Observation') await checkBloodPressure(d.patient, d.id);
}).listen(8799);
```

Comment nous l'avons testé : quatre lectures matinales (136/86, 144/92, 149/94, 152/95) ont été écrites sur un patient synthétique sur une instance locale de la plateforme, et la plateforme a produit un événement `resource.created` signé par lecture. Ces événements, signés avec le code de signature propre à la plateforme, ont été envoyés au service ci-dessus avec deux doublons et une requête signée avec un mauvais secret. Le résultat : la mauvaise signature a obtenu `400`, les doublons ont été ignorés, et le service a déclenché exactement une alerte — `152/95, 149/94, 144/92` — pas une par événement.

En production, conservez `seen` et `alerted` dans une base de données plutôt qu'en mémoire, afin qu'un redémarrage ne répète pas les alertes.

**Réception des webhooks pendant le développement.** Anpheros accepte et ne livre qu'à des points de terminaison HTTPS publics : une adresse `localhost` ou privée est refusée lors de son enregistrement (`400`, champ `url`), même dans le bac à sable, et chaque livraison vérifie à nouveau l'adresse. Pour recevoir des événements sur votre ordinateur portable, exposez le service via un tunnel HTTPS public et enregistrez l'adresse du tunnel. Voir [Webhooks](https://developers.anpheros.com/fr/guides/webhooks).

## Ce que l'enregistrement doit montrer par la suite

L'alerte elle-même réside dans votre service de surveillance, mais l'enregistrement doit montrer ce qu'il en est résulté, afin que le prochain clinicien et le patient voient l'histoire complète :

- les relevés, chacun marqué comme données de l'appareil avec son système source ([provenance](https://developers.anpheros.com/fr/guides/medical-data-provenance)) ;
- un `CarePlan` avec le plan de surveillance (ce qui est mesuré, à quelle fréquence, l'objectif) ;
- un `Encounter` (classe `VR` pour un contact à distance) lorsque l'équipe appelle le patient, et tout changement de médication comme un `MedicationStatement`.

Anpheros stocke ces ressources ; il n'a pas de ressource d'alerte ou de tâche propre (`Flag`, `Task` et `Communication` ne font pas partie de ses types de ressources), donc les files d'attente d'alertes et le routage des gardes appartiennent à votre service.

## Facturation, réglementation et sécurité

Les programmes de RPM dépendent souvent des règles de remboursement — aux États-Unis, Medicare paie pour la surveillance physiologique à distance sous des codes CPT spécifiques avec un nombre minimum de jours de données et de temps passé par l'équipe de soins. Ces règles changent et ne sont pas quelque chose qu'une plateforme de données peut certifier ; vérifiez les règles actuelles du CMS et conservez les preuves (les relevés et le temps de l'équipe) dans votre propre système. Les logiciels qui interprètent les relevés pour orienter le diagnostic ou le traitement peuvent être réglementés en tant que dispositif médical. Gardez les seuils entre les mains des cliniciens et enregistrez chaque alerte et réponse.

## Où Anpheros s'intègre

Anpheros fournit l'enregistrement (Observations FHIR R4 avec provenance, CarePlan, Encounter), des webhooks signés avec tentatives de relance, le consentement si l'application d'une autre organisation a besoin de lire les relevés, et un journal d'accès consultable par le patient. Il ne se connecte pas aux fournisseurs d'appareils, ne décide pas de ce qui est anormal, n'envoie pas d'alertes et ne gère pas la facturation.

## Foire aux questions

### Quelle ressource FHIR stocke une mesure de tension artérielle à domicile ?
Une Observation avec LOINC 85354-9 (panel de tension artérielle) et deux composantes, 8480-6 (systolique) et 8462-4 (diastolique), chacune étant une `valueQuantity` dans `mm[Hg]`, avec l'heure de la mesure et l'appareil comme auteur.

### Les données RPM doivent-elles être des moyennes journalières ou des lectures individuelles ?
Lectures individuelles pour les mesures cliniques telles que la tension artérielle, le poids ou la glycémie, car les cliniciens les examinent et les comptent. Les résumés journaliers conviennent aux données de haute fréquence des wearables telles que la fréquence cardiaque ou les pas.

### Comment déclencher une alerte lorsqu'une nouvelle lecture arrive ?
Abonnez-vous aux webhooks `resource.created`, vérifiez la signature, récupérez la nouvelle Observation et évaluez votre règle sur la série récente. Les livraisons peuvent se répéter, alors dédupliquez par identifiant d'événement et rendez l'alerte idempotente.

### Anpheros peut-il envoyer des alertes aux cliniciens ?
Non. Il notifie votre service via des webhooks ; qui est alerté, comment et quand est décidé dans votre système.

## Liens Utiles

- [Données des appareils portables en FHIR](https://developers.anpheros.com/fr/guides/wearable-data-fhir)
- [Soins virtuels et médecins IA : les données dont ils ont besoin](https://developers.anpheros.com/fr/guides/virtual-care-ai-data)
- [Webhooks](https://developers.anpheros.com/fr/guides/webhooks)
- [Provenance des données médicales expliquée](https://developers.anpheros.com/fr/guides/medical-data-provenance)
- [Intégrations de soins de santé](https://developers.anpheros.com/fr/guides/healthcare-integrations)
