> ## Documentation Index
> Fetch the complete documentation index at: https://docs.telepatia.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Recibe notificaciones de eventos firmadas y verifica su autenticidad

Los webhooks permiten que tu backend reciba notificaciones de eventos en lugar de hacer polling.
Registras un endpoint HTTPS; Telepatia le envía por POST un sobre JSON firmado cada vez que ocurre
un evento suscrito.

Cada entrega se firma con HMAC-SHA256 para que puedas comprobar que proviene de Telepatia. Registra
un webhook con `POST /v1/webhooks`; la respuesta devuelve un `signingSecret` **una sola vez** —
guárdalo ahora.

## Events

### Event catalog

| Evento                     | Estado     | Se dispara cuando                                             |
| -------------------------- | ---------- | ------------------------------------------------------------- |
| `scribe_session.created`   | Disponible | Se crea una nueva sesión de scribe                            |
| `scribe_session.completed` | Disponible | Una sesión de scribe finaliza y su registro médico está listo |
| `scribe_session.error`     | Disponible | Una sesión de scribe falla al procesarse                      |
| `scribe_session.updated`   | Disponible | Se edita el registro de una sesión ya finalizada              |
| `scribe_session.cancelled` | Disponible | Una sesión de scribe se descarta durante la grabación         |
| `scribe_session.deleted`   | Disponible | Se elimina una sesión de scribe                               |

Suscríbete a uno o más eventos por webhook (el array `events` en la creación).

### Subscribing to events

Pasa un array `events` al registrar un webhook para elegir exactamente qué eventos recibe. El campo
`type` de cada entrega indica qué evento se disparó, así un mismo endpoint maneja todo el ciclo.

```bash theme={null}
curl -X POST https://scribe-api.telepatia.ai/v1/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/scribe",
    "events": ["scribe_session.created", "scribe_session.completed", "scribe_session.error"]
  }'
```

Actualiza la suscripción en cualquier momento con `PATCH /v1/webhooks/{id}` y un nuevo array `events`.

### Event envelope

El cuerpo de cada POST de webhook es un sobre JSON. `id` es estable entre reentregas — deduplica
por él. `data` lleva el objeto de negocio del evento — para los eventos `scribe_session.*`, una
instantánea de la sesión:

```json theme={null}
{
  "id": "evt_01HXAMPLE",
  "type": "scribe_session.completed",
  "createdAt": "2026-06-23T12:00:00Z",
  "data": {
    "scribeSessionId": "ss_01HXAMPLE",
    "medicalRecordConfigurationId": "mrc_01HXAMPLE",
    "scribeSessionConfigurationId": null,
    "status": "completed",
    "institutionId": "inst_01HXAMPLE",
    "account": {
      "id": "acc_01HXAMPLE",
      "name": "Dr. Juan Salazar",
      "email": "dr.salazar@clinica.com"
    },
    "patient": {
      "id": "pat_01HXAMPLE",
      "name": "María Pérez",
      "idCountry": "COLOMBIA",
      "idType": "CC",
      "idValue": "1023456789"
    },
    "createdAt": "2026-06-23T11:40:00Z",
    "completedAt": "2026-06-23T12:00:00Z"
  }
}
```

El objeto `data` para los eventos `scribe_session.*`:

| Campo                          | Tipo           | Descripción                                                                                                                                           |
| ------------------------------ | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `scribeSessionId`              | string         | Id público de la sesión de scribe (`ss_…`).                                                                                                           |
| `medicalRecordConfigurationId` | string \| null | Id público de la configuración de registro médico usada (`mrc_…`). Mutuamente excluyente con `scribeSessionConfigurationId`.                          |
| `scribeSessionConfigurationId` | string \| null | Id público de la configuración de sesión de scribe usada (`ssc_…`). Mutuamente excluyente con `medicalRecordConfigurationId`.                         |
| `status`                       | string         | Estado final de la sesión — `completed`, `completedWithErrors`, `error`, `cancelled` o `deleted`.                                                     |
| `institutionId`                | string         | Institución dueña de la sesión.                                                                                                                       |
| `account`                      | object \| null | Profesional tratante — `id` (`acc_…`), `name`, `email`.                                                                                               |
| `patient`                      | object \| null | Paciente — `id`, `name` e identidad `idCountry` / `idType` / `idValue` (mismos campos que la petición de set-consultation-context, para correlación). |
| `createdAt`                    | string         | Cuándo se creó la sesión (ISO 8601).                                                                                                                  |
| `completedAt`                  | string \| null | Cuándo finalizó la sesión (ISO 8601); `null` para eventos de error.                                                                                   |

Se envía la misma forma de `data` para cada evento `scribe_session.*`; `status` refleja el estado
real de la sesión y `completedAt` es `null` cuando la sesión no se completó.

## Verifying the signature

Cada solicitud lleva un encabezado `X-Scribe-Api-Signature` (estilo Stripe):

```
X-Scribe-Api-Signature: t=1750000000,v1=5257a869e7ec...
```

`t` es la marca de tiempo Unix en que se firmó la solicitud; `v1` es el HMAC-SHA256 en hexadecimal
de `"{t}." + raw_request_body`, con clave tu secreto de firma. Verifica sobre el cuerpo **crudo**
de la solicitud antes de parsearlo — reserializar el JSON puede cambiar los bytes y romper la
comparación. Usa una comparación de tiempo constante y rechaza marcas de tiempo fuera de una
ventana de 5 minutos para mitigar ataques de repetición.

```python theme={null}
import hashlib
import hmac
import time

def verify(secret: str, header: str, raw_body: bytes) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    t, v1 = parts["t"], parts["v1"]
    signed = f"{t}.".encode() + raw_body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, v1) and abs(time.time() - int(t)) <= 300
```

Cada solicitud también lleva `X-Scribe-Api-Event-Id` (igual al `id` del sobre) para registro rápido.

## Retry & delivery

* Devuelve un `2xx` rápidamente para confirmar. Haz el trabajo lento de forma asíncrona.
* Un `5xx`, error de red o timeout se reintenta con backoff exponencial (≈2s hasta ≈2min entre
  intentos) por hasta 6 intentos a lo largo de \~15 minutos.
* Cualquier `4xx` (incluidos `408` y `429`) se trata como permanente — la entrega **no** se
  reintenta.
* Los reintentos reutilizan el mismo `id` del sobre, así que deduplica por él para ser idempotente.

## Rotating the signing secret

Llama a `POST /v1/webhooks/{id}/rotate-secret` para generar un nuevo secreto. La respuesta devuelve
el nuevo `signingSecret` una sola vez e incrementa `signingSecretVersion`. El secreto anterior deja
de verificar de inmediato, así que despliega el nuevo valor en tu endpoint antes de rotar.
