> ## 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

> Receba notificações de eventos assinadas e verifique sua autenticidade

Webhooks permitem que seu backend receba notificações de eventos em vez de fazer polling. Você
registra um endpoint HTTPS; a Telepatia envia por POST um envelope JSON assinado sempre que um
evento assinado ocorre.

Cada entrega é assinada com HMAC-SHA256 para que você possa provar que veio da Telepatia. Registre
um webhook com `POST /v1/webhooks`; a resposta retorna um `signingSecret` **uma única vez** —
guarde-o agora.

## Events

### Event catalog

| Evento                     | Status     | Dispara quando                                                 |
| -------------------------- | ---------- | -------------------------------------------------------------- |
| `scribe_session.created`   | Disponível | Uma nova sessão de scribe é criada                             |
| `scribe_session.completed` | Disponível | Uma sessão de scribe termina e seu registro médico está pronto |
| `scribe_session.error`     | Disponível | Uma sessão de scribe falha ao processar                        |
| `scribe_session.updated`   | Disponível | O registro de uma sessão já finalizada é editado depois        |
| `scribe_session.cancelled` | Disponível | Uma sessão de scribe é descartada durante a gravação           |
| `scribe_session.deleted`   | Disponível | Uma sessão de scribe é excluída                                |

Assine um ou mais eventos por webhook (o array `events` na criação).

### Subscribing to events

Envie um array `events` ao registrar um webhook para escolher exatamente quais eventos ele recebe. O
campo `type` de cada entrega indica qual evento disparou, então um único endpoint trata todo o 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"]
  }'
```

Atualize a assinatura a qualquer momento com `PATCH /v1/webhooks/{id}` e um novo array `events`.

### Event envelope

O corpo de cada POST de webhook é um envelope JSON. `id` é estável entre reentregas — deduplique
por ele. `data` carrega o objeto de negócio do evento — para os eventos `scribe_session.*`, um
instantâneo da sessão:

```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"
  }
}
```

O objeto `data` para os eventos `scribe_session.*`:

| Campo                          | Tipo           | Descrição                                                                                                                                           |
| ------------------------------ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `scribeSessionId`              | string         | Id público da sessão de scribe (`ss_…`).                                                                                                            |
| `medicalRecordConfigurationId` | string \| null | Id público da configuração de registro médico usada (`mrc_…`). Mutuamente exclusivo com `scribeSessionConfigurationId`.                             |
| `scribeSessionConfigurationId` | string \| null | Id público da configuração de sessão de scribe usada (`ssc_…`). Mutuamente exclusivo com `medicalRecordConfigurationId`.                            |
| `status`                       | string         | Status final da sessão — `completed`, `completedWithErrors`, `error`, `cancelled` ou `deleted`.                                                     |
| `institutionId`                | string         | Instituição dona da sessão.                                                                                                                         |
| `account`                      | object \| null | Profissional responsável — `id` (`acc_…`), `name`, `email`.                                                                                         |
| `patient`                      | object \| null | Paciente — `id`, `name` e identidade `idCountry` / `idType` / `idValue` (mesmos campos da requisição de set-consultation-context, para correlação). |
| `createdAt`                    | string         | Quando a sessão foi criada (ISO 8601).                                                                                                              |
| `completedAt`                  | string \| null | Quando a sessão terminou (ISO 8601); `null` para eventos de erro.                                                                                   |

A mesma forma de `data` é enviada para cada evento `scribe_session.*`; `status` reflete o estado
real da sessão e `completedAt` é `null` quando a sessão não foi concluída.

## Verifying the signature

Cada requisição carrega um cabeçalho `X-Scribe-Api-Signature` (estilo Stripe):

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

`t` é o timestamp Unix em que a requisição foi assinada; `v1` é o HMAC-SHA256 em hexadecimal de
`"{t}." + raw_request_body`, com chave o seu segredo de assinatura. Verifique sobre o corpo **cru**
da requisição antes de fazer o parse — reserializar o JSON pode mudar os bytes e quebrar a
comparação. Use uma comparação de tempo constante e rejeite timestamps fora de uma janela de 5
minutos para mitigar ataques de repetição.

```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 requisição também carrega `X-Scribe-Api-Event-Id` (igual ao `id` do envelope) para log rápido.

## Retry & delivery

* Retorne um `2xx` rapidamente para confirmar. Faça o trabalho lento de forma assíncrona.
* Um `5xx`, erro de rede ou timeout é repetido com backoff exponencial (≈2s até ≈2min entre
  tentativas) por até 6 tentativas ao longo de \~15 minutos.
* Qualquer `4xx` (incluindo `408` e `429`) é tratado como permanente — a entrega **não** é
  repetida.
* As repetições reutilizam o mesmo `id` do envelope, então deduplique por ele para ser idempotente.

## Rotating the signing secret

Chame `POST /v1/webhooks/{id}/rotate-secret` para gerar um novo segredo. A resposta retorna o novo
`signingSecret` uma única vez e incrementa `signingSecretVersion`. O segredo anterior para de
verificar imediatamente, então implante o novo valor no seu endpoint antes de rotacionar.
