VitrinaAPI

Identificar y fusionar pacientes

Leer y fusionar fichas de identidad, y ver quién responde por un paciente.

ClínicasSolo en los workspaces de clínicas.

GET /clinic/patients lista a las personas que la clínica atiende, con su identidad, su número de ficha y quién puede agendar o leer por ellas. Lo que se le hizo a esa persona está en Ficha clínica. Saber que alguien es paciente de una clínica ya es un dato de salud, y por eso este capítulo entero está marcado como sensible.

Este capítulo pide su propio permiso

Todo lo que está aquí exige clinic_patients:read, o clinic_patients:write para escribir, además del permiso de clínica. clinic:read no alcanza, aunque sea el permiso corriente de la agenda: responde 403 FORBIDDEN nombrando el que falta. Y ninguna aplicación conectada llega, tenga los permisos que tenga: para ella la respuesta es 403 CONNECTED_APP_SENSITIVE_DATA (Datos personales y de salud).

Leer el registro

curl "https://api.vitrinadev.com/api/v1/clinic/patients?limit=2" \
  -H "Authorization: Bearer $VITRINA_KEY"
{
  "data": {
    "data": [
      {
        "id": "12121212-0000-4000-8000-000000000001",
        "source": "native",
        "external_id": null,
        "rut": "12.345.678-5",
        "nombre": "María José",
        "apellidos": "Fuentes Lagos",
        "email": "[email protected]",
        "phone": "+56987654321",
        "birthdate": null,
        "nombre_social": null,
        "prevision": null,
        "numero_ficha": "44110",
        "direccion": "Av. Siempre Viva 742",
        "enabled": true,
        "contact_id": "22222222-0000-4000-8000-000000000001",
        "created_at": "2026-09-22T18:08:29.046Z",
        "updated_at": "2026-09-22T18:08:29.046Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 2
  }
}

Se busca por q, que cubre nombre, RUT y número de ficha. Se filtra por enabled y se pagina con page y limit.

Lo que conviene saber de cada campo:

  • source dice de dónde viene la ficha. native es una ficha de Vitrina; healthatom o reservo, una que vive en el sistema de la clínica y que Vitrina refleja. external_id es cómo la llama ese sistema.
  • nombre_social es el nombre con el que la persona pide que la llamen, y es el que hay que mostrar cuando está.
  • numero_ficha es el número con el que la clínica la busca en la recepción. No es el id, y no es único entre sistemas.
  • contact_id es la persona con la que la clínica conversa por WhatsApp o correo. Un paciente sin contacto es una ficha que todavía nadie puede contactar.

Crear y editar

curl -X POST https://api.vitrinadev.com/api/v1/clinic/patients \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: alta-mjfuentes-2026-09-22" \
  -d '{
    "nombre": "María José",
    "apellidos": "Fuentes Lagos",
    "rut": "12.345.678-5",
    "phone": "+56987654321",
    "email": "[email protected]"
  }'

POST /clinic/patients crea una ficha nativa. Una ficha reflejada se crea en el sistema que la lleva. Si la clínica corre Medilink o Reservo, un alta por esta ruta crearía una segunda persona que ese sistema no conoce. PATCH /clinic/patients/{id} edita, y DELETE sólo borra una ficha nativa sin historia colgando.

Manda siempre una Idempotency-Key al crear. Sin ella, un reintento después de un corte de red no te devuelve la misma ficha: te crea otra persona (Errores).

Duplicados y fusión

GET /clinic/patients/duplicates agrupa las fichas que parecen la misma persona y dice por qué lo parecen: mismo RUT, mismo teléfono, nombre y fecha de nacimiento. Quien decide ve el motivo antes que el veredicto.

{ "data": { "groups": [], "scanned": 1, "truncated": false } }

Fusionar es POST /clinic/patients/{id}/merge, y nunca es un borrado. La ficha absorbida queda como lápida apuntando a la que sobrevive, así que un id que tu sistema ya guardó sigue resolviendo. La historia, las citas y los documentos se mueven a la ficha ganadora en una sola transacción.

Quién responde por un paciente

Un paciente no siempre se representa a sí mismo: un menor tiene apoderado, una carga tiene titular, alguien agenda por su pareja. Ese vínculo es explícito y tiene permisos propios:

{
  "data": {
    "data": [
      {
        "id": "33333333-0000-4000-8000-000000000001",
        "clinic_patient_id": "12121212-0000-4000-8000-000000000001",
        "contact_id": "22222222-0000-4000-8000-000000000001",
        "related_patient_id": null,
        "role": "titular",
        "is_primary": true,
        "can_book": true,
        "can_read_record": false,
        "verified_method": "manual",
        "verified_at": "2026-09-22T18:08:29.053Z",
        "expires_at": null,
        "expired": false
      }
    ]
  }
}

can_book y can_read_record son dos permisos distintos: una madre puede agendar por su hijo de dieciséis sin leerle la ficha. expires_at cierra el vínculo solo, cuando el hijo cumple la mayoría de edad, y expired lo dice ya calculado para que nadie tenga que comparar fechas.

Las rutas son GET|POST /clinic/patients/{id}/relationships y PATCH|DELETE /clinic/relationships/{id}. Desde el otro lado, GET /clinic/contacts/{contactId}/relationships lista todos los pacientes por los que responde una persona, y GET /clinic/contacts/{contactId}/patient es la tarjeta «Paciente» de la ficha de contacto.

Lo que queda registrado

Cada lectura de este capítulo escribe una línea en el registro de accesos de la ficha, en la misma transacción que la respuesta. Si el registro no se puede escribir, la lectura falla. La línea dice quién leyó, qué ruta usó y sobre qué ficha, sea una API key o la persona detrás de un token personal.

Con ese registro la clínica le responde a un paciente qué se hizo con sus datos. Se lee en GET /clinic/patients/{id}/record-events, que además del permiso de ficha pide clinic_admin:write: quién miró una ficha lo revisa la administración.

Eventos

EventoCuándo
clinic_patient.created · .updatedSe dio de alta o se editó una ficha
clinic_patient.deletedSe borró una ficha nativa sin historia
clinic_patient.mergedDos fichas eran la misma persona; trae la que sobrevive

Todos llegan como aviso: traen el id y data_omitted: "sensitive", nunca el cuerpo, aunque la suscripción pida los datos del recurso. Para leer la ficha hay que llamar a la API con una credencial que tenga el permiso. Esa llamada pasa por el registro de accesos (Webhooks).

Lo que no está publicado

POST /clinic/patients/{id}/promote y las dos rutas de link son el vínculo con HealthAtom y no forman parte de la API pública.

En esta página