Webhook de estado del caso
TrustCare avisa al sistema de un socio (por ejemplo, el HIS de una clínica o una aseguradora) cada vez que cambia el estado del caso de un paciente. El aviso es un POST firmado a la URL que el socio registre.
Estado: propuesta (v0.1)El contrato está en revisión con los socios y todavía no está desplegado. Puede cambiar antes de la v1.
Qué casos llegan a cada socio
Cada socio recibe solo los eventos de los casos en los que participa. Una clínica o su HIS recibe los de sus pacientes, y una aseguradora, los de sus asegurados. Una suscripción equivale a un socio, un entorno, una URL y un secreto de firma.
Cómo se genera
El aviso se escribe en la misma transacción que el cambio de estado. Si el cambio se revierte, el aviso no sale; si se confirma, sale aunque falle algo justo después. Así un estado nunca queda sin aviso ni se avisa uno que no ocurrió.
Registro
Por cada entorno (pruebas y producción), el socio nos indica:
- la URL HTTPS que recibirá los eventos;
- qué identificadores propios quiere recibir en
partnerRefs(ver abajo); - un contacto técnico para incidencias.
TrustCare le devuelve el secreto de firma por un canal seguro. El secreto se rota cuando el socio lo pida o ante cualquier sospecha de filtración.
Evento
{
"id": "evt_01JC9W3K7Q",
"event_type": "CASE_STATUS_CHANGED",
"occurredAt": "2026-11-20T14:05:00-05:00",
"sequence": 4,
"case": {
"ref": "6f1d2c3e-8a4b-4c5d-9e6f-0a1b2c3d4e5f",
"clinicId": "1b2c3d4e-5f60-4718-8293-a4b5c6d7e8f9",
"procedureCode": "PROC_BICHECTOMIA",
"partnerRefs": {
"hisEpisodeId": "EP-2026-118842"
}
},
"status": {
"from": "CASE_SCHEDULED",
"to": "CASE_PRE_ANESTHESIA_OK",
"patientPhase": "CLINICAL_PHASE_PRE_OP_READY"
},
"estimatedDate": "2026-11-27"
}| Campo | Qué es |
|---|---|
id | Id único del evento. Se repite si el mismo evento se reenvía. |
event_type | Hoy solo CASE_STATUS_CHANGED. |
occurredAt | Cuándo cambió el estado, con zona horaria. |
sequence | Número del evento dentro del caso. Crece en 1 con cada evento del mismo caso. |
case.ref | Id del caso en TrustCare. Es un identificador seudónimo: no contiene datos del paciente. |
case.clinicId | Clínica donde se atiende el caso. |
case.procedureCode | Código del paquete o procedimiento contratado, del catálogo comercial de TrustCare. |
case.partnerRefs | Identificadores que el propio socio nos dio para ese caso, para que lo ubique en su sistema. Por ejemplo, hisEpisodeId para un HIS, o osiguBeneficiaryId y osiguAuthorizationId para OSIGU. Cada socio solo recibe los suyos. |
status.from, status.to | Estado anterior y nuevo. Ver Estados del caso. |
status.patientPhase | Fase que ve el paciente en la app de TrustCare. |
estimatedDate | Fecha estimada del procedimiento (YYYY-MM-DD), o null si aún no hay. |
Solo datos comercialesEn esta versión el evento lleva únicamente datos comerciales del caso: el paquete contratado, su estado y la fecha estimada. No lleva nombre, documento de identidad, datos de contacto, diagnósticos ni historia clínica del paciente.
Los datos sensibles se habilitarán solo al integrarnos con el HIS de cada clínica, con la autorización expresa del paciente y el acuerdo de tratamiento de datos firmado. Ver Ciclo de vida y tratamiento de los datos.
Respuesta esperada
Responde 2xx en menos de 10 segundos. Recomendamos 201 con el id recibido y tu propio id de registro, que guardamos para soporte y auditoría:
{ "id": "evt_01JC9W3K7Q", "partner_transaction_id": "TX-998812" }Valida la firma y guarda el evento antes de responder. Cualquier otro procesamiento hazlo después, en segundo plano.
Reintentos
Si la respuesta no es 2xx o no llega a tiempo, TrustCare reintenta con espera creciente: a los 1 min, 5 min, 30 min, 2 h, 6 h, 12 h y 24 h. Son 8 intentos en unas 45 horas. Si se agotan, el aviso queda marcado como no entregado y la clínica puede reenviarlo; ver Ciclo de vida y tratamiento de los datos.
Verificar la firma
Cabeceras de cada envío:
X-TrustCare-Event-Id: el mismoiddel cuerpo.X-TrustCare-Signature: t=<epoch en segundos>,v1=<hmac>: HMAC-SHA256 en hexadecimal del texto<t>.<cuerpo tal cual llegó>, con tu secreto.
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyTrustCareSignature(rawBody, header, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const received = Buffer.from(parts.v1 ?? '', 'hex');
const wanted = Buffer.from(expected, 'hex');
return received.length === wanted.length && timingSafeEqual(received, wanted);
}- Calcula la firma sobre el cuerpo sin parsear: si lo vuelves a serializar, cambia y la firma no coincide.
- Rechaza firmas de más de 5 minutos: evita que alguien reenvíe un evento capturado.
- Compara siempre en tiempo constante, como en el ejemplo.
Duplicados y orden
- Duplicados: un mismo evento puede llegar más de una vez. Descarta los
idya procesados. - Orden: los eventos pueden llegar desordenados. Ignora los que traigan un
sequencemenor o igual al último que procesaste de ese caso. - Reconciliación: si sospechas que perdiste eventos, pídenos el estado actual del caso. Los socios con API de consulta lo tienen en
GET /cases/{caseRef}/status.
Updated about 1 hour ago

