Panorama
El agente es un servicio HTTP que corre dentro de su infraestructura, junto a la base de datos. Su aplicación le pregunta antes de entregar un dato personal, y el agente responde tres cosas: si el acceso es lícito, qué campos corresponden a la finalidad invocada y dónde quedó la constancia.
La integración tiene tres piezas, y sólo la primera es código suyo:
- La llamada. Su aplicación declara quién pide, para qué y qué recurso.
- El mapeo. Un archivo que dice qué tabla y qué columna es cada dato, y de qué categoría.
- El paquete normativo. El régimen aplicable, como configuración. Para salud ya existe.
El agente es determinista: la misma solicitud con el mismo paquete produce siempre la misma decisión. No usa aprendizaje automático. Cada decisión se puede explicar citando la regla que la tomó.
Autenticación
Toda ruta, salvo la de salud del servicio, exige la cabecera X-Consta-Key con la clave del agente. Sin clave o con una clave errónea la respuesta es 401 y no se registra nada: un tercero sin clave no puede llenar la cadena de eventos.
Content-Type: application/json
X-Consta-Key: <clave del agente>Autorizar un acceso
POST /v1/authorize responde la decisión y los nombres de los campos habilitados, sin leer ningún valor. Sirve para saber qué está permitido antes de pedir el recurso.
{
"actor": {
"id": "prof_alfa_07",
"rol": "medico_tratante",
"organizacion": "org_alfa"
},
"finalidad": "asistencia_sanitaria",
"titular": "pac_00042",
"recurso": {"tipo": "condicion", "id": "cnd_00042"},
"campos": ["diagnostico"]
}| Campo | Qué es |
|---|---|
| actor.id | Identificador opaco del profesional. Obligatorio. Nunca su RUT ni su nombre. |
| actor.rol | El rol con que actúa, en el vocabulario del paquete. |
| actor.organizacion | La organización a la que pertenece. Un actor sólo accede a recursos de la suya. |
| finalidad | Para qué se pide el dato. Sin finalidad admisible, se deniega. |
| recurso | El tipo y el identificador del recurso, tal como los declara el mapeo. |
| campos | Los campos que se piden. Nunca se entrega uno que no se pidió. |
Lo que la petición no decide
Ni la procedencia ni la categoría las decide quien pide. La procedencia la resuelve el agente en la fuente, recurso por recurso; la categoría la declara el mapeo, campo por campo. Si la petición las trae, se aceptan pero no deciden, y si difieren de lo resuelto la discrepancia queda registrada.
{
"decision": "denegar",
"motivo": "procedencia_prohibida",
"articulo": "Ley 19.628, art. 16 bis inc. 3°",
"detalle": [],
"paquete": "clinguard@0.7.1",
"registro": {"id": 34, "hash": "a7217279…8c7e54"}
}articulo viene sólo cuando la norma funda la decisión, y siempre tomado del paquete. Las denegaciones que nacen de la lista blanca, como una combinación que nadie habilitó, llevan null: atribuirles un artículo sería decir que la ley tomó una decisión que no tomó.
Autorizar y leer en una operación
POST /v1/resources/read recibe la misma petición, autoriza y entrega los valores de los campos habilitados. Es la forma recomendada de integrarse: la aplicación nunca recibe el registro completo para decidir después qué mostrar.
{
"decision": "permitir",
"recurso": {
"tipo": "paciente",
"id": "pac_00002",
"nombre": "Javiera",
"telefono": "+56 9 0000 5487"
},
"omitidos": [
{"campo": "prevision", "motivo": "no_habilitado", "categoria": "prevision"},
{"campo": "rut", "motivo": "no_mapeado", "categoria": null}
],
"paquete": "clinguard@0.7.1",
"registro": {"id": 35, "hash": "3d7df4c4…73727096"}
}Una recepcionista pidió nombre, teléfono y previsión para agendar. La previsión está en la misma fila, pero es otra categoría y esa finalidad no la habilita: se omite, y ni siquiera se pide a la base.
omitidos es evidencia, no información para el usuario final: son los nombres de lo que el recurso tenía y no se entregó, con su motivo. Nunca valores. Esa diferencia es la que permite demostrar después que la minimización operó.
Ejemplos con datos sintéticos. Ningún dato de esta página corresponde a una persona real.
Códigos de respuesta
Un cliente que sólo mira el código falla de forma segura: todo lo que no es 200 es «no entregar».
| Código | Cuándo | ¿Queda registrado? |
|---|---|---|
| 200 | Permitido. | Sí |
| 403 | Denegado. Incluye el recurso de otra organización y el titular con un bloqueo vigente. | Sí |
| 404 | El recurso no existe en la fuente. | Sí |
| 409 | No se pudo establecer la procedencia o la organización del recurso: no se llegó a evaluar. | Sí |
| 422 | Solicitud inválida, o tipo de recurso que el mapeo no declara. | Sí, sin el cuerpo recibido |
| 401 | Sin clave o con clave errónea. | No |
| 503 | El registro o la fuente no están disponibles. El agente no decide. | No: no hubo decisión |
El 503 es una garantía, no una falla: sin constancia no hay decisión. Su aplicación debe tratarlo como «no entregar ahora» y reintentar.
El mapeo de la fuente
El agente no sabe qué guarda cada tabla: se lo dice un archivo. Por cada tipo de recurso declara la tabla, la clave, la columna con la procedencia, la columna con la organización y cada campo entregable con su categoría.
entidades:
paciente:
tabla: paciente
clave: id
procedencia: procedencia
organizacion: organizacion_id
titular: id
campos:
nombre: {columna: nombres, categoria: administrativo}
telefono: {columna: telefono, categoria: administrativo}
prevision: {columna: prevision, categoria: prevision}La categoría va por campo y no por tabla, porque una misma tabla mezcla categorías. Una columna que no aparece en el mapeo no tiene categoría y nunca se entrega: es la forma de dejar fuera el RUT o el correo sin escribir una regla.
Sólo lectura
El agente se conecta con un rol que sólo puede leer. Al arrancar inspecciona los privilegios de ese rol y, si puede escribir en alguna tabla, se detiene. Cada conexión se abre además en modo de sólo lectura.
El paquete normativo
El régimen aplicable es un archivo declarativo que se valida al cargar. Declara las finalidades admisibles, las procedencias admitidas y prohibidas y la matriz de habilitación. Lo que no habilita expresamente se deniega.
paquete: <id>
version: <semver>
base_legal: [...]
categorias: {...}
finalidades_admisibles: # lista cerrada
- id: <id>
articulo: <referencia> # sin artículo, el paquete no carga
requiere: [<condicion>] # opcional
procedencias_declaradas:
<id>:
prohibida: <bool>
articulo: <referencia> # obligatorio si está prohibida
alcance: [<categoria>] # opcional: a qué categorías alcanza
matriz_habilitacion: [...] # rol, finalidad, categoría, procedencia, campos
reglas_deteccion: [...] # opcionalUn integrador normalmente no escribe un paquete: usa el de su sector. Lo que sí conviene saber es que cada respuesta dice qué paquete y qué versión decidió, en el campo paquete.
Verificar la cadena
Cada decisión se anexa a un registro de sólo anexado, encadenado por SHA-256: cada evento guarda la huella del anterior. El campo registro de cada respuesta trae la posición del evento y su huella.
GET /v1/audit/verify recorre la cadena de punta a punta. Si alguien alteró un evento, eliminó uno o reescribió la historia, la verificación lo detecta e indica la causa: contenido_alterado, continuidad_rota o enlace_roto.
La consola hace la misma comprobación desde fuera, sin ver el registro: guarda la raíz de la cadena en cada sincronización y la compara con la siguiente.
Despliegue
El agente se entrega como contenedor y se levanta con Docker Compose, junto a un PostgreSQL propio para el registro. Su base de datos no se modifica: el agente sólo la lee.
- Un rol de sólo lectura en su base, para el agente.
- El archivo de mapeo de su fuente.
- El paquete normativo de su sector.
- Las claves del agente, que deben ser distintas entre sí: el agente no arranca si se repiten.
El repositorio incluye un entorno sintético completo, con una clínica de ejemplo, para probar la integración sin tocar datos reales.
Consta es un proyecto de título validado con datos sintéticos; no hay un servicio alojado. Para acceder al repositorio o coordinar una prueba, escriba a contacto@consta.live.