AnpherosAnpheros PlatformDevelopers

Try it free in the sandbox · first FHIR call in 5 minutesFree · 30 synthetic patients · no card

Cómo importar resultados de laboratorio a un registro FHIR

Tutorial: importar un informe de laboratorio como CSV y como HL7 v2 ORU^R01 a Observaciones FHIR R4 con códigos LOINC y rangos de referencia, manejar marcadores no mapeados e informes reenviados, y rastrear cada resultado a su laboratorio.

Este tutorial toma un informe de laboratorio como archivo CSV y como mensaje HL7 v2 ORU^R01, lo importa a un registro HL7 FHIR R4 de un paciente en una solicitud cada uno, y muestra lo que se obtiene a cambio: recursos de laboratorio Observation con códigos LOINC, rangos de referencia y procedencia. También cubre los dos problemas con los que se encuentra toda integración de laboratorio: marcadores que no se pueden mapear automáticamente e informes que se envían dos veces. Todo se ejecuta en el sandbox gratuito con un paciente sintético.

Por qué los resultados de laboratorio son difíciles de almacenar

Un informe de laboratorio parece simple: un marcador, un valor, una unidad, un rango de referencia, una fecha. En la práctica, cada laboratorio nombra los marcadores a su manera ("Hemoglobina glicozilada", "HbA1c", "Glycated haemoglobin"), usa comas o puntos para los decimales, escribe rangos como 4,0-5,6, <5 o >3.5, y reenvía el mismo informe cuando algo falla. Almacenados como texto libre, esos resultados no se pueden comparar a lo largo del tiempo, graficar, compartir con otra aplicación o entregar a un modelo de IA con confianza.

Un registro FHIR almacena cada resultado como un Observation con un código LOINC, una unidad UCUM, un valor, un rango de referencia y una fecha, y mantiene quién lo envió en un recurso Provenance. El conector de laboratorio realiza esa conversión por usted.

 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

Antes de empezar

Necesitas una clave de sandbox (sk_test_…) de platform.anpheros.com/dashboard (Obtener una clave de sandbox). Los ejemplos utilizan curl; los SDKs tienen las mismas llamadas (labs.importCsv, labs.importHl7, labs.importItems).

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')

Paso 1: importar un informe CSV

Así es como un laboratorio rumano podría exportar un informe: punto y coma, comas decimales, nombres de columna rumanos y fechas como DD.MM.YYYY. El conector reconoce nombres de columna rumanos e ingleses.

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 respuesta (201):

{"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, glucosa y TSH se mapearon a LOINC a partir de sus nombres. Las comas decimales se convirtieron en números y los rangos se convirtieron en referenceRange. Una fila regresó en unmapped. Todavía se almacena, bajo el código propio del laboratorio (urn:anpheros:lab-code) con el texto exacto del informe, por lo que no se pierde nada.

Paso 2: Manejar marcadores no mapeados

Algunos nombres requieren una decisión. "Glicemie" (glucosa en sangre) es un buen ejemplo: LOINC tiene un código para la glucosa en suero o plasma (2345-7, una muestra venosa tomada en un laboratorio) y otro para la glucosa en sangre total (2339-0, por ejemplo, una punción digital). En un informe de laboratorio, un "Glicemie" sin más se mapea a suero o plasma. Un nombre que diga "capilar", "capillary" o "sânge integral" se mapea a sangre total:

{"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"}]}

Para los marcadores que el conector no reconoce, busque el código:

curl "$BASE/v1/terminology/loinc?q=glucose" -H "Authorization: Bearer $KEY"
{"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}

Luego, envíe el código en una columna LOINC. Un código en el informe siempre prevalece sobre el nombre. Mantenga ese mapeo por laboratorio de su lado y agregue la columna a cada informe de ese laboratorio:

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

Los marcadores que LOINC no cubre en absoluto, como el inventado "Marker foarte rar", pueden permanecer bajo el código del laboratorio. Se muestran al paciente y se incluyen en el registro. No se pueden comparar con los resultados de otros laboratorios.

Paso 3: enviar el mismo informe dos veces

Los laboratorios reenvían informes. Cada fila obtiene un identificador estable construido a partir del ID del informe, el código, la fecha y el valor, por lo que una segunda importación del mismo informe no crea nada:

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

Las filas que cambiaron, un valor corregido por ejemplo, se importan como nuevos resultados. La cabecera Idempotency-Key añade una segunda garantía: un reintento de red con la misma clave devuelve la primera respuesta sin cambios.

Paso 4: importar un mensaje HL7 v2 ORU^R01

La mayoría de los sistemas de información de laboratorio utilizan HL7 v2. Envíe el mensaje tal como está (texto ER7, segmentos separados por CR o 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
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
{"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 con el sistema de codificación LN se utiliza directamente como LOINC. OBX-7 se convierte en el rango de referencia y OBX-8 (H, N) la interpretación. OBR-3 (R-77) se mantiene como el ID del informe en la procedencia. El paciente es el de la URL: el segmento PID no se utiliza para encontrar un paciente, por lo que un PID incorrecto no puede adjuntar resultados a otra persona.

¿Ya tiene resultados analizados? Envíe JSON: {"date": "2026-10-01", "origin": "report-77", "items": [{"marker": "Ferritin", "value": 38, "unit": "ng/mL", "ref_low": 15, "ref_high": 150}]}. La ferritina se mapea a LOINC 2276-4.

Paso 5: leer los resultados como FHIR

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

El HbA1c importado, tal como se almacena:

{
  "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": "%"}}]
}

El LOINC display es el nombre estándar en inglés. code.text conserva la redacción propia del laboratorio, por lo que el paciente sigue viendo lo que estaba en el informe en papel. Para más información sobre recursos y búsquedas, consulte la guía de la plataforma FHIR y los sistemas de códigos FHIR.

Paso 6: Comprobar quién lo envió

curl "$BASE/v1/patients/$PID/provenance/Observation/62b1b0c0-…" -H "Authorization: Bearer $KEY"
{"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 y source_system: labx permanecen con el valor. Cuando una aplicación de IA lea posteriormente este resultado a través de la API de contexto, se etiquetará como [import/labx], de modo que el modelo pueda distinguir un resultado de laboratorio de un valor introducido por el paciente.

Qué sucede a continuación

Preguntas frecuentes

¿Qué formatos de informes de laboratorio se pueden importar a FHIR?

CSV con una fila de encabezado (se aceptan coma, punto y coma o tabulación, coma decimal), mensajes HL7 v2 ORU^R01 y JSON. Los informes PDF se pueden almacenar como documentos; los valores extraídos de ellos apuntan de vuelta al documento.

¿Qué sucede con los marcadores sin código LOINC?

Se almacenan bajo el código propio del laboratorio con el texto exacto del informe y se listan en unmapped. Agregue una columna LOINC para ese laboratorio y se mapearán. Un código en el informe siempre tiene prioridad sobre el nombre.

¿Es seguro reenviar un informe?

Sí. Cada fila tiene un identificador estable construido a partir del informe, código, fecha y valor, por lo que las filas ya importadas se omiten; las filas modificadas se importan como nuevos resultados.

¿Puede un laboratorio importar resultados para cualquier paciente?

No. Un laboratorio importa en los pacientes creados por su proyecto o cuyos registros la persona compartió con él a través de OAuth. Otros pacientes no existen para él (404).

Relacionado

Try it free in the sandbox · first FHIR call in 5 minutesFree · 30 synthetic patients · no card

Product updates by email

New features, guides and the end of the beta. One email a month at most.

Send me occasional Anpheros Platform updates by email (new features, API changes, guides). I'll confirm by email and can unsubscribe in one click from any email. Privacy