Los recursos FHIR son pequeños porque las referencias hacen el trabajo
Los recursos FHIR son deliberadamente estrechos. Una Observation no incorpora el paciente al que pertenece: apunta a él. Una Condition no incorpora al clínico que la afirmó: apunta a él. Un Encounter no incorpora la organización que prestó el servicio: apunta a ella. Eso es lo que mantiene los recursos pequeños, cacheables y actualizables de forma independiente, y es lo que convierte a las referencias en el elemento estructural sobre el que descansa todo el estándar.
También significa que un recurso aislado suele ser ininterpretable. Una Observation con "subject": {"reference": "Patient/034AB16"} te dice que se tomó una medida, pero no sobre quién, hasta que esa referencia se resuelve. Cuando no se resuelve —porque el destino nunca se incluyó, porque la referencia es un identificador provisional de la transacción de otro, porque un sistema reasignó ids al ingerir— aparece la clase de fallo de integración más común en FHIR: datos estructuralmente válidos y semánticamente huérfanos.
Esta guía cubre las formas que puede adoptar una referencia, cómo se resuelve cada una, las reglas especiales dentro de Bundles y transacciones, y qué hacer cuando las referencias no resuelven. Todos los ejemplos se pueden pegar en el Explorador de Bundles FHIR, que construye el mapa de referencias localmente en tu navegador.
El tipo de dato Reference
Allí donde un recurso apunta a otro, FHIR usa el tipo Reference. No es solo una cadena: tiene cuatro elementos, y conocerlos todos evita bastante confusión.
reference— la referencia literal: una URL que identifica el destino. Es el campo al que todo el mundo se refiere al decir «referencia».type— el tipo de recurso del destino, expresado como URI relativa ahttp://hl7.org/fhir/StructureDefinition/, es decir, en la práctica un código simple como"Patient". Cuando se indica, debe coincidir con el tipo que se obtiene al resolver la referencia.identifier— una referencia lógica: un identificador de negocio del destino en lugar de una dirección.display— una descripción breve y legible del destino, para renderizar cuando no puedas o no quieras resolverlo.
Un Reference puede llevar una referencia literal, una lógica, ambas o ninguna. La página de referencias de R4 es explícita: cuando ambas están presentes se prefiere la literal, y las aplicaciones pueden —pero no están obligadas a— comprobar que ambas concuerdan.
Referencias literales: las tres formas
La especificación R4 indica que Reference.reference contiene una URL que es exactamente una de tres cosas: una URL absoluta, una URL relativa o una referencia interna de fragmento. Todo lo demás de este artículo se deriva de esos tres casos.
Referencias relativas
La forma que más verás: "reference": "Patient/034AB16". Es un tipo de recurso y un id lógico, y se resuelve respecto a la URL base del servicio. En un servidor cuya base sea https://hospital.ejemplo.org/fhir/, esa referencia significa https://hospital.ejemplo.org/fhir/Patient/034AB16.
Las referencias relativas son compactas y portables dentro de un ecosistema: el mismo payload funciona en desarrollo, preproducción y producción porque cambia la base y no las referencias. Su debilidad es precisamente esa portabilidad: una referencia relativa carece de sentido sin conocer la base. Copia una Observation de la respuesta de un servidor a otro sistema y Patient/034AB16 pasará a apuntar en silencio a quienquiera que ocupe ese id en el nuevo servidor. No es hipotético: las colisiones de ids entre sistemas son la razón por la que muchas organizaciones reasignan ids en la frontera, lo que a su vez explica por qué llegan referencias rotas.
Referencias absolutas
La forma autodescriptiva: "reference": "https://hospital.ejemplo.org/fhir/Patient/034AB16". Dice exactamente dónde vive el destino y sobrevive a ser copiada a cualquier parte. La especificación señala que las URL absolutas ofrecen un enfoque estable y escalable, adecuado a un contexto cloud o web, mientras que las relativas encajan mejor en el intercambio dentro de un ecosistema cerrado.
Conviene recordar algunas propiedades. Las referencias absolutas no tienen por qué apuntar a un servidor REST FHIR, aunque la norma lo considera el enfoque preferido. Las URL distinguen mayúsculas y minúsculas siempre: Patient/ABC y Patient/abc son recursos distintos, y una capa intermedia que normalice mayúsculas romperá los enlaces. Y una referencia puede ser específica de versión incluyendo el segmento de historial, como en http://ejemplo.org/fhir/Observation/1x2/_history/2, algo habitual en contextos de procedencia y auditoría donde hay que apuntar exactamente a la versión que se vio, no a la actual.
Referencias urn:uuid
La tercera forma no existe fuera de su contexto. Dentro de un Bundle que estás construyendo puede que aún no sepas qué ids asignará el servidor, así que inventas identidades: el fullUrl de cada entrada se fija a un valor como urn:uuid:c72aa430-2ddc-456e-7a09-dea8264671d8 y las demás entradas referencian ese URN. La página de referencias de R4 indica que, en una transacción, las URL de referencia pueden contener URIs lógicas —OIDs o UUIDs— que se resuelven dentro de la transacción, y que al procesarla el servidor sustituye la URL lógica por la URL literal correcta al finalizar.
La propiedad crucial de una referencia urn:uuid: es que solo tiene sentido dentro de su propio Bundle. Extrae una entrada de un Bundle de transacción y guárdala por separado, y sus referencias pasarán a apuntar a nada, en ninguna parte. Es un fallo sorprendentemente frecuente en pipelines ETL que dividen Bundles en registros por recurso sin un paso de resolución.
Recursos contained: referencias que apuntan hacia dentro
A veces el destino de una referencia no puede existir de forma independiente. La especificación R4 pone un ejemplo preciso: un motor de integración que construye una Condition a partir de un mensaje HL7 v2 solo conoce el nombre y apellido del cirujano principal, tomados del segmento REL. Sin un directorio de profesionales controlado, eso no basta para crear un Practitioner identificado —puede haber varios con el mismo nombre— y cualquier id que inventaras carecería de sentido fuera de esa Condition concreta.
Para estos casos FHIR permite colocar el recurso en línea dentro del elemento contained del padre y referenciarlo con un fragmento: "reference": "#p1", apuntando al recurso contenido cuyo id es p1. Una referencia de solo "#" apunta al propio contenedor, que es como un Provenance contenido señala a su padre.
La norma impone reglas firmes a la contención, y son las que más se incumplen:
- Un recurso contenido no puede contener a su vez otros recursos contenidos. La contención tiene siempre un solo nivel.
- Las referencias a recursos contenidos nunca se resuelven fuera del recurso contenedor. La resolución se detiene en
Bundle.entry.resourceyParameters.parameter.resource. - Los recursos contenidos no pueden llevar
meta.versionId,meta.lastUpdatednimeta.security. Sí pueden llevarmeta.tag. - Un recurso solo debe contenerse si algo del contenedor lo referencia, o si el recurso contenido referencia al contenedor.
- Los recursos contenidos no heredan contexto del padre: un recurso contenido con su propio elemento
subjectno afirma que comparta el sujeto del padre. - La versión de FHIR de un recurso contenido es siempre la misma que la de su contenedor.
La especificación también es tajante sobre cuándo no contener: no debe hacerse cuando el contenido puede identificarse correctamente, porque una vez perdida la identidad es extremadamente difícil restaurarla. La contención es un recurso último para datos genuinamente no identificables, no un atajo para evitar un segundo POST.

Resolver referencias dentro de un Bundle
La página de Bundle de R4 define un algoritmo explícito de resolución de referencias dentro de un Bundle, y empieza con la regla más importante: las aplicaciones que leen un Bundle deben buscar el recurso por su identidad dentro del Bundle antes de intentar acceder a él externamente. El Bundle es el universo local; solo si una referencia falla ahí entra en juego el mundo exterior.
El procedimiento, parafraseando la norma, es el siguiente. Si la referencia no es ya absoluta, conviértela: cuando tiene la forma [tipo]/[id] y el fullUrl de la entrada que contiene el recurso que referencia es una URL REST, extrae la raíz de ese fullUrl y añádele la referencia, y luego resuelve el resultado dentro del Bundle como una URL absoluta. Si la referencia ya es absoluta, busca una entrada cuyo fullUrl coincida; si ninguna coincide y la URI es una URL resoluble, puede recuperarse directamente. Si la referencia es específica de versión, elimina la versión antes de comparar con fullUrl y luego empareja la versión usando Resource.meta.versionId. Las reglas para resolver referencias dentro de recursos contenidos son las mismas que las del recurso contenedor. Y si coinciden varias entradas, es ambiguo cuál es la correcta: las aplicaciones pueden devolver un error o actuar como consideren adecuado.
De ahí se derivan dos consecuencias. Primera: la base que se usa para una referencia relativa procede del fullUrl de la propia entrada que referencia, no de un ajuste global; un Bundle ensamblado a partir de dos servidores puede contener legítimamente referencias relativas que se resuelven contra dos bases distintas. Segunda: cuando la entrada que referencia no tiene fullUrl, o este no es una URL REST, la referencia relativa no puede convertirse, y la norma dice sin rodeos que entonces la referencia no tiene significado definido dentro de la especificación.
Reescritura de referencias en una transacción
Las transacciones añaden un mecanismo más. Cuando un servidor procesa una transacción y asigna un id nuevo a un recurso enviado con POST, debe además actualizar todas las referencias a ese recurso dentro del mismo Bundle a medida que se procesan. Las referencias a recursos que no forman parte del Bundle se dejan intactas.
El alcance de esa reescritura es más amplio de lo que la mayoría espera. La norma indica que el servidor debe sustituir todos los enlaces coincidentes dondequiera que aparezcan: en ids de recurso, en referencias, en elementos de tipo uri, url, oid y uuid, e incluso dentro de los atributos href y src de la narrativa. Los elementos de tipo canonical quedan explícitamente excluidos.
Hay otro mecanismo disponible solo dentro de una transacción: las referencias condicionales. En lugar de un identificador provisional, una referencia puede ser una URI de búsqueda que describe cómo encontrar el destino, por ejemplo "reference": "Patient?identifier=12345". La norma señala que esto surge con frecuencia al construir transacciones a partir de mensajes v2, donde el cliente conoce un NHC pero no un id lógico FHIR. Al procesar, el servidor revisa todas las referencias en busca de URIs de búsqueda, ejecuta la búsqueda y, si hay exactamente una coincidencia, sustituye la URI por una referencia a ella. Si no hay coincidencias o hay más de una, la transacción falla. Ese comportamiento de todo o nada es una virtud: una coincidencia ambigua de paciente debe detener una escritura, no adivinarla.
Ten en cuenta también que, al procesar un POST, el servidor trata el fullUrl como el id del sistema origen y lo ignora, generando el suyo. En actualizaciones intenta mapear el fullUrl recibido a una URL local, y si no tiene mapeo ignora la base y asume la suya. Y un servidor puede simplemente asignar ids nuevos a todo, con independencia de cualquier id lógico declarado, porque respetar los ids del cliente solo es seguro en circunstancias controladas.
Por qué las referencias no resueltas rompen integraciones
Una referencia no resuelta rara vez provoca un fallo estruendoso. Es peor: es pérdida silenciosa de datos que aflora más tarde, en un análisis o en una vista clínica, como ausencia y no como error. Las causas habituales merecen enumerarse porque cada una tiene una solución distinta.
- El destino simplemente no se incluyó. Una búsqueda devolvió Observations sin
_include=Observation:subject, así que todas las referencias al sujeto apuntan fuera del Bundle. Los datos no están mal; la extracción está incompleta. - Identificadores provisionales fuera de su Bundle. Se dividió una transacción en recursos individuales sin resolver antes las referencias
urn:uuid:. Ahora esas referencias carecen de sentido de forma permanente. - Se reasignaron ids al ingerir. El sistema receptor generó sus propios ids —como permite la norma— pero una copia posterior de los datos conservó las referencias originales. Todos los enlaces apuntan ahora al recurso equivocado o a ninguno.
- Desajuste de URL base. Las referencias relativas se resolvieron contra la base equivocada porque las entradas no llevaban
fullUrl, o porque un pipeline aplanó Bundles de varios orígenes en un único fichero. - Deriva de mayúsculas o barras finales. Las URL distinguen mayúsculas, y un proxy normalizador o un fixture editado a mano puede romper enlaces por lo demás perfectos.
- Referencias contained tratadas como globales. Una referencia
#p1solo significa algo dentro de su contenedor; extraída, no resuelve a nada.
El remedio es el mismo en todos los casos: medirlo. Antes de que un Bundle entre en un pipeline, cuenta cuántas referencias resuelven localmente e inspecciona las que no, y decide deliberadamente si cada una es esperable —un puntero legítimo a un recurso que vive en un servidor— o un defecto.
Mapear referencias sin enviar datos a ninguna parte
El Explorador de Bundles FHIR se construyó alrededor de este problema. Indexa cada cadena reference de lo que pegues —un Bundle, un recurso suelto, NDJSON de una exportación masiva o un array JSON— y resuelve cada una contra los recursos presentes, emparejando por TipoDeRecurso/id, por id desnudo, por el fullUrl de la entrada y por el segmento final Tipo/id de ese fullUrl, que es como las entradas de un mismo Bundle suelen direccionarse entre sí. Obtienes las referencias salientes por recurso, las entrantes por recurso y una lista explícita de todo lo que no resolvió.
Dos decisiones de diseño son deliberadas y conviene enunciarlas. Las referencias no resueltas se muestran, nunca se descartan: todo el objetivo es ver los agujeros, así que eliminar en silencio un enlace roto arruinaría el ejercicio. Y la resolución es solo local, nunca por red: un Bundle son datos de paciente, y recuperar en silencio un recurso referenciado desde un servidor externo supondría filtrarlos. Todo se ejecuta en tu navegador; no se sube nada.
Es una herramienta de exploración, no un verificador de conformidad: no te dirá que un perfil exigía un elemento que omitiste. Para eso, pasa el mismo recurso por el Validador de Recursos FHIR. Y si todavía estás averiguando qué clase de Bundle tienes y por qué sus fullUrl tienen ese aspecto, empieza por Qué es un Bundle FHIR. Si tus referencias llegaron en NDJSON desde una extracción poblacional en lugar de en un Bundle, La exportación Bulk Data de FHIR explicada cubre por qué ese formato resuelve de forma distinta.
Conclusión
Una referencia FHIR es una de tres cosas: una URL absoluta que dice exactamente dónde vive un recurso, una URL relativa que se resuelve contra una base que hay que conocer, o un fragmento que apunta hacia dentro, a un recurso contenido. Dentro de un Bundle, la resolución mira primero en local, convirtiendo las relativas con el fullUrl de la propia entrada que referencia. Dentro de una transacción, las identidades provisionales urn:uuid: y las referencias condicionales de búsqueda las reescribe el servidor al asignar ids reales, tanto en referencias como en ids, elementos de tipo uri y enlaces de la narrativa.
Con esas reglas claras, la mayoría de los problemas de referencias dejan de ser un misterio. Después, convierte las referencias no resueltas en algo que mides y no en algo que descubres en producción: abre el fichero en el Explorador de Bundles FHIR, mira qué no resolvió y decide si cada hueco es esperable, con los datos de paciente sin salir de tu máquina.