# Comment importer les résultats de laboratoire dans un dossier FHIR

> Tutoriel : importer un rapport de laboratoire au format CSV et au format HL7 v2 ORU^R01 dans des Observations FHIR R4 avec des codes LOINC et des plages de référence, gérer les marqueurs non mappés et les rapports renvoyés, et tracer chaque résultat jusqu'à son laboratoire.

Source: https://developers.anpheros.com/fr/guides/import-lab-results-fhir

**Ce tutoriel prend un rapport de laboratoire sous forme de fichier CSV et de message HL7 v2 ORU^R01, l'importe dans le dossier HL7 FHIR R4 d'un patient en une seule requête chacun, et montre ce que vous obtenez en retour : des ressources de laboratoire `Observation` avec des codes LOINC, des plages de référence et la provenance.** Il couvre également les deux problèmes que toute intégration de laboratoire rencontre : les marqueurs qui ne peuvent pas être mappés automatiquement et les rapports qui sont envoyés deux fois. Tout fonctionne sur le bac à sable gratuit avec un patient synthétique.

## Pourquoi les résultats de laboratoire sont difficiles à stocker

Un rapport de laboratoire semble simple : un marqueur, une valeur, une unité, une plage de référence, une date. En pratique, chaque laboratoire nomme les marqueurs à sa manière ("Hemoglobina glicozilata", "HbA1c", "Glycated haemoglobin"), utilise des virgules ou des points pour les décimales, écrit les plages comme `4,0-5,6`, `<5` ou `>3.5`, et renvoie le même rapport en cas d'échec. Stockés sous forme de texte libre, ces résultats ne peuvent pas être comparés dans le temps, tracés, partagés avec une autre application ou donnés à un modèle d'IA avec confiance.

Un enregistrement FHIR stocke chaque résultat comme un `Observation` avec un code LOINC, une unité UCUM, une valeur, une plage de référence et une date, et conserve qui l'a envoyé dans une ressource `Provenance`. Le [connecteur de laboratoire](https://developers.anpheros.com/fr/guides/lab-connector) effectue cette conversion pour vous.

```
 lab report (CSV, HL7 v2, JSON)
        │  POST /v1/patients/{id}/labs/import
        ▼
 ┌──────────────────────────────────────────────┐
 │ parse rows → map marker to LOINC → parse     │
 │ value, unit, range → stable id per row       │
 └──────────────────────────────────────────────┘
        │
        ▼
 Observation (category laboratory) + Provenance (author_type import, source system, report id)
        │
        ├─► the patient's app and every app they consented to
        └─► webhooks: resource.created per Observation
```

## Avant de commencer

Vous avez besoin d'une clé sandbox (`sk_test_…`) depuis [platform.anpheros.com/dashboard](https://platform.anpheros.com/dashboard/) (**Obtenir une clé sandbox**). Les exemples utilisent `curl` ; les [SDK](https://developers.anpheros.com/fr/guides/sdks) ont les mêmes appels (`labs.importCsv`, `labs.importHl7`, `labs.importItems`).

```bash
export BASE=https://platform.anpheros.com
export KEY=sk_test_…
# Elena Ionescu, a synthetic patient with type 2 diabetes
export PID=$(curl -s "$BASE/fhir/R4/Patient?identifier=https://anpheros.com/fhir/sid/sandbox-persona|p02" \
  -H "Authorization: Bearer $KEY" | jq -r '.entry[0].resource.id')
```

## Étape 1 : importer un rapport CSV

Voici comment un laboratoire roumain pourrait exporter un rapport : points-virgules, virgules décimales, noms de colonnes roumains et dates comme `DD.MM.YYYY`. Le connecteur reconnaît les noms de colonnes roumains et anglais.

```bash
cat > report.csv <<'EOF'
Analiza;Rezultat;UM;Interval de referinta;Data
Hemoglobina glicozilata (HbA1c);6,9;%;4,0-5,6;01.10.2026
Glicemie;131;mg/dL;70-100;01.10.2026
TSH;2,1;mUI/L;0,4-4,0;01.10.2026
Marker foarte rar;12;U/L;0-20;01.10.2026
EOF

curl -X POST "$BASE/v1/patients/$PID/labs/import" -H "Authorization: Bearer $KEY" \
     -H "Content-Type: text/csv" -H "X-Anpheros-Source-System: labx" \
     -H "Idempotency-Key: report-2026-10-01-elena" --data-binary @report.csv
```

La réponse (`201`) :

```json
{"created": 4, "skipped": 0, "unmapped": ["Marker foarte rar"], "warnings": [],
 "observations": [
  {"id": "ff0bbb44-…", "marker": "Hemoglobina glicozilata (HbA1c)", "code": "4548-4", "system": "http://loinc.org", "value": 6.9, "unit": "%", "date": "2026-10-01"},
  {"id": "ebe5522f-…", "marker": "Glicemie", "code": "2345-7", "system": "http://loinc.org", "value": 131.0, "unit": "mg/dL", "date": "2026-10-01"},
  {"id": "bace44a8-…", "marker": "TSH", "code": "3016-3", "system": "http://loinc.org", "value": 2.1, "unit": "mUI/L", "date": "2026-10-01"},
  {"id": "26fd2270-…", "marker": "Marker foarte rar", "code": "marker_foarte_rar", "system": "urn:anpheros:lab-code", "value": 12.0, "unit": "U/L", "date": "2026-10-01"}]}
```

HbA1c, glucose et TSH ont été mappés à LOINC à partir de leurs noms. Les virgules décimales sont devenues des nombres, et les plages sont devenues `referenceRange`. Une ligne est revenue dans `unmapped`. Elle est toujours stockée, sous le code propre au laboratoire (`urn:anpheros:lab-code`) avec le texte exact du rapport, de sorte que rien n'est perdu.

## Étape 2 : gérer les marqueurs non mappés

Certains noms nécessitent une décision. "Glicemie" (glycémie) en est un bon exemple : LOINC a un code pour le glucose dans le sérum ou le plasma (`2345-7`, un échantillon veineux prélevé en laboratoire) et un autre pour le glucose dans le sang total (`2339-0`, par exemple une piqûre au doigt). Sur un rapport de laboratoire, un simple "Glicemie" est mappé au sérum ou au plasma. Un nom indiquant "capilar", "capillary" ou "sânge integral" est mappé au sang total :

```json
{"created": 1, "skipped": 0, "unmapped": [], "warnings": [],
 "observations": [{"id": "00480a6d-…", "marker": "Glicemie capilara", "code": "2339-0", "system": "http://loinc.org", "value": 140.0, "unit": "mg/dL", "date": "2026-10-02"}]}
```

Pour les marqueurs que le connecteur ne reconnaît pas, recherchez le code :

```bash
curl "$BASE/v1/terminology/loinc?q=glucose" -H "Authorization: Bearer $KEY"
```

```json
{"data": [
  {"system": "http://loinc.org", "code": "2339-0", "display": "Glucose [Mass/volume] in Blood", "display_ro": "Glicemie (sânge)", "category": "laboratory", "unit": "mg/dL"},
  {"system": "http://loinc.org", "code": "2345-7", "display": "Glucose [Mass/volume] in Serum or Plasma", "display_ro": "Glicemie (ser)", "category": "laboratory", "unit": "mg/dL"}],
 "total": 2}
```

Envoyez ensuite le code dans une colonne `LOINC`. Un code dans le rapport l'emporte toujours sur le nom. Conservez ce mappage par laboratoire de votre côté et ajoutez la colonne à chaque rapport provenant de celui-ci :

```
Analiza;Rezultat;UM;Interval de referinta;Data;LOINC
Glicemie;99;mg/dL;70-100;04.10.2026;2339-0
```

Les marqueurs que LOINC ne couvre pas du tout, comme le "Marker foarte rar" inventé, peuvent rester sous le code du laboratoire. Ils sont présentés au patient et inclus dans le dossier. Ils ne peuvent pas être comparés aux résultats d'autres laboratoires.

## Étape 3 : envoyer le même rapport deux fois

Les laboratoires renvoient les rapports. Chaque ligne obtient un identifiant stable construit à partir de l'ID du rapport, du code, de la date et de la valeur, de sorte qu'une deuxième importation du même rapport ne crée rien :

```json
{"created": 0, "skipped": 4, "unmapped": ["Marker foarte rar"], "warnings": [], "observations": []}
```

Les lignes qui ont changé, une valeur corrigée par exemple, sont importées comme de nouveaux résultats. L'en-tête `Idempotency-Key` ajoute une deuxième garantie : une nouvelle tentative réseau avec la même clé renvoie la première réponse inchangée.

## Étape 4 : Importer un message HL7 v2 ORU^R01

La plupart des systèmes d'information de laboratoire utilisent HL7 v2. Envoyez le message tel quel (texte ER7, segments séparés par CR ou LF) :

```
MSH|^~\&|LABX|LAB1|ANPHEROS|EHR|202610010930||ORU^R01|MSG00077|P|2.5.1
PID|1||12345||Ionescu^Elena||19850903|F
OBR|1||R-77|24331-1^Lipid panel^LN|||202610010800
OBX|1|NM|2093-3^Cholesterol [Mass/volume] in Serum or Plasma^LN||212|mg/dL|<200|H|||F|||202610010800
OBX|2|NM|2085-9^HDL Cholesterol^LN||52|mg/dL|>40|N|||F|||202610010800
```

```bash
curl -X POST "$BASE/v1/patients/$PID/labs/import" -H "Authorization: Bearer $KEY" \
     -H "Content-Type: x-application/hl7-v2+er7" --data-binary @oru.hl7
```

```json
{"created": 2, "skipped": 0, "unmapped": [], "warnings": [],
 "observations": [
  {"id": "b562045b-…", "marker": "Cholesterol [Mass/volume] in Serum or Plasma", "code": "2093-3", "system": "http://loinc.org", "value": 212.0, "unit": "mg/dL", "date": "2026-10-01T08:00:00Z"},
  {"id": "9b3274e4-…", "marker": "HDL Cholesterol", "code": "2085-9", "system": "http://loinc.org", "value": 52.0, "unit": "mg/dL", "date": "2026-10-01T08:00:00Z"}]}
```

`OBX-3` avec le système de codage `LN` est utilisé directement comme LOINC. `OBX-7` devient la plage de référence et `OBX-8` (`H`, `N`) l'interprétation. `OBR-3` (`R-77`) est conservé comme identifiant de rapport dans la provenance. Le patient est celui de l'URL : le segment `PID` n'est pas utilisé pour trouver un patient, donc un `PID` incorrect ne peut pas attacher de résultats à quelqu'un d'autre.

Vous avez déjà des résultats analysés ? Envoyez du JSON : `{"date": "2026-10-01", "origin": "report-77", "items": [{"marker": "Ferritin", "value": 38, "unit": "ng/mL", "ref_low": 15, "ref_high": 150}]}`. La ferritine est mappée au LOINC `2276-4`.

## Étape 5 : lire les résultats en tant que FHIR

```bash
curl "$BASE/fhir/R4/Observation?patient=$PID&code=http://loinc.org|4548-4&_sort=-date&_count=1" \
     -H "Authorization: Bearer $KEY"
```

L'HbA1c importé, tel qu'il est stocké :

```json
{
  "resourceType": "Observation",
  "id": "62b1b0c0-…",
  "meta": {"versionId": "1", "lastUpdated": "2026-10-12T20:05:57.011Z"},
  "identifier": [{"system": "urn:anpheros:lab-import", "value": "c52d3cd6449291d34f6bef1516d0ac05"}],
  "status": "final",
  "category": [{"coding": [{"system": "http://terminology.hl7.org/CodeSystem/observation-category", "code": "laboratory"}]}],
  "code": {"coding": [{"system": "http://loinc.org", "code": "4548-4", "display": "Hemoglobin A1c/Hemoglobin.total in Blood"}],
           "text": "Hemoglobina glicozilata (HbA1c)"},
  "subject": {"reference": "Patient/…"},
  "effectiveDateTime": "2026-10-01",
  "valueQuantity": {"value": 6.9, "unit": "%"},
  "referenceRange": [{"low": {"value": 4.0, "unit": "%"}, "high": {"value": 5.6, "unit": "%"}}]
}
```

Le nom d'affichage LOINC est le nom standard en anglais. `code.text` conserve la formulation propre au laboratoire, de sorte que le patient voit toujours ce qui figurait sur le rapport papier. Pour en savoir plus sur les ressources et la recherche, consultez le guide de la [plateforme FHIR](https://developers.anpheros.com/fr/guides/fhir) et les [systèmes de codes FHIR](https://developers.anpheros.com/fr/guides/fhir-code-systems).

## Étape 6 : vérifier qui l'a envoyé

```bash
curl "$BASE/v1/patients/$PID/provenance/Observation/62b1b0c0-…" -H "Authorization: Bearer $KEY"
```

```json
{"data": [{"version": 1, "activity": "CREATE", "recorded": "2026-10-12T20:05:57.011000Z",
           "author_type": "import", "author": "proj_…", "source_system": "labx", "origin_id": null,
           "fhir": "Provenance/f15c4fee-…"}], "total": 1}
```

`author_type: import` et `source_system: labx` restent avec la valeur. Lorsqu'une application d'IA lit plus tard ce résultat via l'[API de contexte](https://developers.anpheros.com/fr/guides/llm-healthcare-data), il est étiqueté `[import/labx]`, permettant ainsi au modèle de distinguer un résultat de laboratoire d'une valeur saisie par le patient.

## Et ensuite ?

- Le patient consulte les résultats dans son application. Toute application à laquelle il a consenti peut les lire, dans le cadre de ses scopes ([modèle de consentement et d'accès](https://developers.anpheros.com/fr/guides/consent)).
- Un [webhook](https://developers.anpheros.com/fr/guides/webhooks) `resource.created` est déclenché pour chaque Observation, permettant à un tableau de bord de clinique ou à un résumé IA de se mettre à jour sans interroger.
- `POST /fhir/R4/Observation/$validate?profile=eu-lab` indique comment un résultat se compare au profil de rapport de laboratoire HL7 Europe.
- Jusqu'à 500 lignes par requête ; les limites sont listées dans [Limites et limitations de débit](https://developers.anpheros.com/fr/guides/limits).

## Foire aux questions

### Quels formats de rapports de laboratoire peuvent être importés dans FHIR ?
CSV avec une ligne d'en-tête (virgule, point-virgule ou tabulation, virgule décimale acceptée), messages HL7 v2 ORU^R01 et JSON. Les rapports PDF peuvent être stockés comme documents ; les valeurs extraites d'eux renvoient au document.

### Que deviennent les marqueurs sans code LOINC ?
Ils sont stockés sous le code propre au laboratoire avec le texte exact du rapport et listés dans `unmapped`. Ajoutez une colonne LOINC pour ce laboratoire et ils seront mappés. Un code dans le rapport a toujours la priorité sur le nom.

### Est-il sûr de renvoyer un rapport ?
Oui. Chaque ligne possède un identifiant stable construit à partir du rapport, du code, de la date et de la valeur, de sorte que les lignes déjà importées sont ignorées ; les lignes modifiées sont importées comme de nouveaux résultats.

### Un laboratoire peut-il importer des résultats pour n'importe quel patient ?
Non. Un laboratoire importe dans les patients son projet créé ou dont les enregistrements ont été partagés avec lui via OAuth. Les autres patients n'existent pas pour lui (`404`).

## Liens connexes

- [API des résultats de laboratoire](https://developers.anpheros.com/fr/guides/lab-results-api) : HL7 v2 et FHIR côte à côte, et ce qu'un résultat doit contenir
- [Connecteur de laboratoire](https://developers.anpheros.com/fr/guides/lab-connector) : chaque nom de colonne, champ HL7 et champ de réponse
- [Interopérabilité des données de santé](https://developers.anpheros.com/fr/guides/interoperability) : pourquoi les codes et la provenance rendent les données échangées utilisables
- [Intégrations de santé](https://developers.anpheros.com/fr/guides/healthcare-integrations) : cliniques, laboratoires, appareils et applications tierces
- [API de données médicales](https://developers.anpheros.com/fr/guides/medical-data-api) : écriture d'observations uniques, de conditions et de médicaments
- [Comment créer une application de santé IA avec un backend FHIR](https://developers.anpheros.com/fr/guides/build-ai-healthcare-app) : utilisation de ces résultats dans une réponse IA
