Auvral

Webhooks

Cómo Auvral le cuenta a tu backend lo que pasó: entrega, reintentos, idempotencia y por qué esta es la única señal autoritativa.

Los webhooks son la forma en que tu backend se entera del resultado de una verificación. Hoy son la única forma, así que trata a tu manejador de webhooks como el lugar donde la verificación cambia el estado de tu sistema.

Configura uno

Dashboard, Integraciones, Webhooks, Crear webhook.

CampoNotas
NombrePara tu propia referencia. Hasta 100 caracteres.
URL del webhookTiene que ser https://.
FlujosPara qué flujos recibe eventos este webhook. Al menos uno.
Secreto de firmaDéjalo en blanco para generar uno, o pon el tuyo.

Un webhook está suscrito a flujos, no a tipos de evento: recibe los cuatro tipos de evento de las verificaciones de ese flujo. Filtra por type en tu manejador.

Revela el secreto de firma desde la página del webhook y guárdalo junto a tu llave de API. Es una credencial aparte, con su propia rotación.

Enviar evento de prueba entrega un evento webhook.test con los mismos headers y el mismo esquema de firma que uno real. Úsalo para confirmar que tu endpoint es alcanzable y para desarrollar contra él.

Los cuatro eventos

typeCuándo
verification.startedTu usuario empezó la verificación
verification.submittedTu usuario terminó de enviar; todavía no hay veredicto
verification.succeededVeredicto: verificado
verification.failedVeredicto: rechazado

Más webhook.test, enviado solo por el botón de prueba del dashboard.

El evento de éxito es verification.succeeded

No verification.completed. Si estás trabajando con una guía de integración vieja o con un ejemplo que menciona verification.completed o un cuerpo plano { verificationId, sessionId, event }, está desactualizado. Ver Referencia de eventos.

verification.failed es una verificación rechazada, no un fallo de entrega. Es un resultado normal.

Cuerpo del evento

Todos los eventos tienen el mismo sobre:

{
  "id": "6f1d0a4c-8e22-4b7e-9a13-5c0be9f4d871",
  "type": "verification.succeeded",
  "createdAt": "2026-08-25T15:12:44.301Z",
  "data": {
    "verification": {
      "id": "b47ac10b-58cc-4372-a567-0e02b2c3d479",
      "status": "VERIFIED",
      "flowId": "3f1c9a52-4d7e-4b3a-9c21-8e5f6a0b7d34",
      "flowName": "Onboarding",
      "sessionId": "9b2f4c1e-7a83-4d06-b5f1-2c9e8d3a6047",
      "referenceId": "user_8842",
      "metadata": { "orderId": "order-8841" },
      "startedAt": "2026-08-25T15:11:02.884Z",
      "endedAt": "2026-08-25T15:12:44.120Z",
      "steps": [
        { "name": "document_verification", "status": "VERIFIED" },
        { "name": "biometric_verification", "status": "VERIFIED" }
      ]
    }
  }
}

Ubica a tu usuario con data.verification.sessionId (el sessionId que guardaste cuando creaste la sesión) o con data.verification.referenceId (tu propio id de la persona).

El cuerpo no lleva ningún dato personal: ni nombre, ni número de documento, ni imagen.

El detalle campo por campo está en la Referencia de eventos.

Headers

HeaderValor
x-auvral-signaturev1=<hex>, HMAC-SHA256 de `${timestamp}.${rawBody}`
x-auvral-timestampSegundos Unix; el mismo valor que entró en la firma
x-auvral-event-idEl id del evento. Estable entre reintentos, deduplica con este
x-auvral-event-typepor ejemplo verification.succeeded
x-auvral-delivery-idEste intento de entrega en particular
x-auvral-attemptNúmero de intento
user-agentAuvral-Webhooks/2

Verifica la firma. Esto no es opcional.

La URL de tu webhook es alcanzable por cualquiera que la descubra, y la firma es lo único que separa un evento real de Auvral de uno falsificado que dice éxito.

Tres reglas, y las tres le han mordido a integraciones reales:

  1. Lee el cuerpo crudo antes de parsearlo. La firma cubre bytes exactos. Cualquier framework que te entregue un objeto ya parseado destruyó lo que necesitas: un objeto JSON re-serializado no va a coincidir.
  2. Rechaza marcas de tiempo viejas. Sin una comprobación de frescura, una petición capturada se puede reproducir contra ti para siempre. Cinco minutos es una ventana razonable.
  3. Compara en tiempo constante. crypto.timingSafeEqual en Node, hmac.compare_digest en Python.

Verificar firmas tiene código que funciona en Node, Python, Go, PHP y Ruby.

Entrega, reintentos e idempotencia

  • Éxito es cualquier 2xx. Cualquier otra cosa, un 4xx, un 5xx, un timeout o un fallo de DNS, es un intento fallido.
  • El timeout es de 10 segundos por intento.
  • Hasta 6 intentos, con esperas de 1 minuto, 5 minutos, 30 minutos, 2 horas y luego 6 horas entre ellos. Un endpoint que está caído toda una tarde igual recibe sus eventos.
  • El cuerpo de tu respuesta se guarda y se ve en el dashboard, así que un mensaje de error útil te facilita tu propia depuración.

Haz que tu manejador sea idempotente

Los reintentos reutilizan el mismo x-auvral-event-id, así que un manejador que no sea idempotente puede procesar la misma verificación hasta seis veces: seis correos de aprobación, seis cuentas acreditadas. Registra el id del evento, e ignora uno que ya hayas procesado.

Confirma rápido. Devuelve 2xx apenas hayas verificado la firma y guardado el evento de forma durable; haz el trabajo de verdad después, fuera de línea. Un manejador que hace trabajo lento en línea va a chocar con el timeout de 10 segundos y va a recibir un reintento, que es como empieza el problema de los duplicados.

Los eventos no están ordenados: un verification.submitted reintentado puede llegar después de verification.succeeded. Guía tu lógica por type y data.verification.status, no por el orden de llegada.

Monitoreo

La página del webhook en el dashboard muestra cada intento de entrega (marca de tiempo, estado HTTP, cuerpo de la respuesta, número de intento) y te deja reintentar uno fallido a mano o pausar el webhook sin borrarlo.

El navegador nunca decide

El postMessage es una señal de interfaz, no una prueba

Nunca desbloquees una cuenta, apruebes un pago o cambies el estado de tu negocio a partir de él. La verdad es el webhook o un GET del lado del servidor. Vive en el navegador, y el usuario controla su navegador.

El callback onResult del SDK llega en el navegador de tu usuario. Es genuinamente útil para actualizar tu interfaz de inmediato, y genuinamente falsificable por la persona sentada frente a él.

La división que hay que sostener:

SeñalConfía en ella paraNunca confíes en ella para
onResult en el navegadorMostrar una pantalla de éxito, avanzar tu interfazEstado de la cuenta, pagos, límites, permisos
Un webhook con firma verificadaTodoNada

Un patrón limpio: onResult consulta a tu propio backend, que reporta éxito solo una vez que tu manejador de webhooks ya corrió.

Reconciliar, no sondear

Un webhook perdido o procesado dos veces sí se puede reconciliar preguntándole a la API después: GET /v1/sessions/{sessionId} y GET /v1/verifications/{id} responden con tu llave de API.

Eso no vuelve consejos las dos cosas de abajo. Verifica las firmas y haz tu manejador idempotente: la lectura es una herramienta de reparación para la entrega que perdiste, no un reemplazo de la entrega. Úsala de forma programada o cuando falte algo, nunca en un bucle mientras corre una verificación, y nunca como aquello que espera la página de tu usuario.

Relacionado

En esta página