Contrato FHIR
El cliente envía recursos FHIR R4 a POST /fhir. La identidad de cada entidad es el
número externo del cliente (no el DNI ni el beneficio PAMI): así el gateway y el backend
correlacionan sin depender de datos de PAMI.
Headers
| Header | Obligatorio | Uso |
|---|---|---|
x-api-key | Sí | Autenticación del cliente (token Ajolote) |
x-source-message-id | Sí | Clave de idempotencia del envío. Repetir el mismo id = duplicado (no reprocesa) |
Systems de identificadores
Los identifier.system acordados (configurables por env, con estos defaults):
| Entidad | System (default) |
|---|---|
| Paciente (nº externo) | https://cliente.example.org/ids/patient |
| Episodio | https://cliente.example.org/ids/encounter |
| Práctica | https://cliente.example.org/ids/procedure |
| Documento | https://cliente.example.org/ids/document |
| Turno | https://cliente.example.org/ids/appointment |
| Beneficio PAMI | https://pami.gob.ar/beneficiario |
| DNI | https://www.argentina.gob.ar/dni |
Referencias por identificador lógico
Las referencias entre recursos se hacen por identifier, no por reference: "Patient/123".
Ejemplo — un episodio que apunta a su paciente:
"subject": {
"type": "Patient",
"identifier": { "system": "https://cliente.example.org/ids/patient", "value": "PAC-00" }
}
Una referencia resuelve si el destino está en el mismo bundle o ya existe en el dominio del gateway.
Extensiones
| Extensión | URL (default) | Dónde | Contenido |
|---|---|---|---|
| Autorización (OP) | .../StructureDefinition/authorization | Encounter / Procedure / Appointment | sub-ext number (nº de OP) |
| Cobertura | .../StructureDefinition/coverage | Encounter | sub-ext credential + plan |
| Uso de diagnóstico | .../StructureDefinition/diagnosis-use | Condition | valueCode |
coverage es de cara al backendEl gateway no modela credencial ni plan: pasa la extensión coverage verbatim en el
payload. Es el backend quien la lee para resolver credencial + plan (ver
El endpoint /inbound). Un episodio sin coverage es aceptado por
el gateway pero rechazado (422) por el backend.
Recursos soportados
Patient, Encounter, Condition, Procedure, Appointment, DocumentReference. Se aceptan
sueltos o dentro de un Bundle (transaction/collection).
Episodio (Encounter) — ejemplo completo
{
"resourceType": "Encounter",
"identifier": [{ "system": "https://cliente.example.org/ids/encounter", "value": "ENC-001" }],
"status": "in-progress",
"class": { "code": "IMP" },
"subject": {
"type": "Patient",
"identifier": { "system": "https://cliente.example.org/ids/patient", "value": "PAC-00" }
},
"period": { "start": "2026-06-01T09:30:00-03:00" },
"extension": [
{
"url": "https://cliente.example.org/fhir/StructureDefinition/coverage",
"extension": [
{ "url": "credential", "valueString": "150123456789-00" },
{ "url": "plan", "valueString": "PAMI_PI" }
]
},
{
"url": "https://cliente.example.org/fhir/StructureDefinition/authorization",
"extension": [{ "url": "number", "valueString": "AUTH-001" }]
}
]
}
Mapeos relevantes:
class.code→ tipo de episodio:IMP= internación (hospitalization),AMB= ambulatorio.status:in-progress= alta/abierto;finished/cancelled= cierre;entered-in-error= baja (soft-delete).
Errores controlados (respuesta síncrona)
Cuando la validación síncrona falla, el gateway responde 422 (o 400 si el payload es
estructuralmente inválido) con un OperationOutcome. El código estable viaja en
issue.details.coding.code:
| Código | Significado |
|---|---|
INVALID_BUNDLE | Payload no es un Bundle/recurso FHIR válido |
UNSUPPORTED_RESOURCE_TYPE | resourceType no soportado |
MISSING_IDENTIFIER / MISSING_REFERENCE | Falta el identificador o la referencia |
PATIENT_NOT_FOUND | El episodio/documento referencia un paciente inexistente |
ENCOUNTER_NOT_FOUND | La práctica/documento referencia un episodio inexistente |
PROCEDURE_NOT_FOUND | El documento referencia una práctica inexistente |
TARGET_NOT_FOUND | El subject.type del documento no es Patient/Encounter/Procedure |
El gateway sólo valida contra lo que él conoce. Reglas que dependen de datos exclusivos del
backend (ej.: que el planCode exista en el catálogo de Proteus) no se validan acá: el
cliente recibe 202 y, si el plan no existe, el mensaje muere después como denied en el
outbox. Cerrar ese hueco requeriría que el gateway conozca el
catálogo de planes.