Diagnostico rapido

Sintoma, causa probable y primer chequeo

Sintoma Causa probable Primer chequeo
No llega ningun webhook Webhook apagado, URL vacia o usuario sin organizacion Revisar `webhookEnabled`, `webhookUrl`, `webhookSecret` y `organizationId`
Llega `record.created` pero no `expedient.completed` `webhookEvents` sin `expedient.completed` o no se dio la transicion correcta Revisar array de eventos y condiciones de `onExpedientWrite`
Firma invalida Body mutado antes de validar o secret incorrecto Comparar raw body, header y secret exacto
Duplicados en el destino Falta de idempotencia en el receptor Persistir `event + id` como llave de dedupe
El destino marca timeouts Respuesta despues de 5 segundos Responder `2xx` antes y procesar asincronamente
Payload distinto al esperado Se esta leyendo la ruta legacy en vez del org-level Buscar `eventId/eventType/data` para detectar envelope legacy

Caso 1

El evento no sale

Checklist para `record.created`
  • El usuario del record tiene `organizationId`.
  • La organizacion existe y tiene URL + secret configurados.
  • `webhookEnabled` es `true` o, en configs antiguas, esta ausente pero URL/secret existen.
  • `webhookEvents` contiene `record.created`, o esta ausente y aplica la compatibilidad.
  • El documento creado cumple el type guard `isRecord`.
Checklist para `expedient.completed`
  • El expediente existia antes del update; no es una creacion pura.
  • `before.isProcessing === true`.
  • `after.isProcessing !== true`.
  • No existe `after.errorMessage`.
  • `webhookEvents` contiene `expedient.completed`.

Caso 2

La firma no valida

Error mas comun

El framework parsea JSON y luego se vuelve a serializar para calcular HMAC. Ese body ya no coincide byte a byte con el original.

Verificacion recomendada

Loguea por separado `x-kapture-signature`, longitud del raw body y digest calculado localmente para comparar.

Secuencia de chequeo

  1. 1
    Confirma que el secret sea el mismo en ambos lados. Evita espacios, truncamientos o valores rotados parcialmente.
  • 2
    Valida el formato del header. Debe ser `sha256=<hex>`.
  • 3
    Usa raw body en bytes o buffer. No uses `JSON.stringify(req.body)` como sustituto si no preservaste el original.
  • 4
    Compara con tiempo constante. En Node usa `crypto.timingSafeEqual`; en Python `hmac.compare_digest`.

    Caso 3

    Llegan duplicados al destino

    Esto no debe tratarse como bug automatico del emisor

    La estrategia segura es que el receptor siempre sea idempotente. La forma minima es guardar `event:id` y rechazar o ignorar repeticiones.

    Llaves recomendadas

    • `record.created` → `record.created:{recordId}`
    • `expedient.completed` → `expedient.completed:{expedientId}`
    • Legacy envelope → se puede guardar tambien `eventId`, pero no reemplaza la dedupe funcional.

    Caso 4

    El endpoint responde tarde o hace timeout

    Lo que suele pasar

    • Se hace escritura lenta al destino antes de responder.
    • Se valida la firma y luego se ejecutan multiples llamadas externas en linea.
    • No hay cola o worker interno del lado receptor.

    Patron recomendado

    • Validar firma.
    • Persistir el evento o ponerlo en cola.
    • Responder `200` o `202`.
    • Procesar en background.

    Caso 5

    Confusion entre org-level y legacy

    Senal Org-level Legacy
    Body Payload publico directo Envelope con `eventId`, `eventType`, `timestamp`, `data`
    User-Agent `Kapture-Webhooks/1.0` `Kapture-Webhook-Bot/1.0`
    Config fuente `organizations/{orgId}` `users/{userId}/integrations/{integrationId}`
    Evento legacy disponible N/A Solo `expedient.completed`

    Soporte

    Bundle minimo para diagnosticar

    • Nombre del evento esperado y hora aproximada.
    • URL destino configurada.
    • Headers capturados (`X-Kapture-Event`, `X-Kapture-Timestamp`, `X-Kapture-Signature`).
    • Status HTTP devuelto por el receptor.
    • Digest esperado local y digest recibido si el problema es de firma.
    • Aclaracion de si se trata del webhook org-level o de una integracion legacy.