Copiar página

Receta

Recibe y verifica los webhooks de AllSign

Objetivo: Un endpoint que recibe los eventos de AllSign, comprueba que de verdad los firmó AllSign y sobrevive a sus propias caídas y a la rotación del secreto.

Para: backend · nivel intermedio · 15 min · Necesitas: API key de sandbox · Un endpoint HTTPS público en tu servidor

Grabado en el entorno interno de AllSign con una key de sandbox · backend 24c0469 · · test docs-checks-v3/tests/recetas/recibe-y-verifica-webhooks.receta.spec.ts

Los ejemplos usan $ALLSIGN_API_KEY. Defínela una vez con tu key de sandbox: export ALLSIGN_API_KEY="allsign_test_sk_…" (en PowerShell: $env:ALLSIGN_API_KEY="allsign_test_sk_…").

1 Consulta qué eventos puedes recibir

GET /webhooks/events ver en la referencia

Qué haces

Suscribe tu endpoint solo a los eventos que vas a procesar: cada uno es una llamada a tu servidor.

Qué mirar

  • data.0.event = "document.created"
  • data.0.description = "A document was created via the API."

2 Registra tu endpoint

POST /webhooks ver en la referencia

Qué haces

La respuesta trae el secreto whsec_… una sola vez: guárdalo como secreto de tu servidor, nunca en el frontend. Con él vas a comprobar que cada entrega viene de AllSign.

Qué mirar

  • id = "whe_27226709c9124d798a44032672fe2c9a"
  • secret = "whsec_…"
  • status = "enabled"

3 Crea un documento para que haya un evento

POST /documents ver en la referencia

Qué haces

Cualquier documento sirve: al crearlo, AllSign le avisa a tu endpoint con document.created.

Qué mirar en sandbox

  • id = "doc_8a9708721fb54fcfabc32715b87f5518"

4 Recibe la entrega y verifica su firma

POST tu endpoint · evento document.created

Qué haces

Antes de creerle al cuerpo, verifica la firma: webhook-signature trae v1,<HMAC-SHA256 en base64> de webhook-id.webhook-timestamp.cuerpo crudo, con tu secreto sin el prefijo whsec_ y decodificado de base64. Rechaza timestamps de más de 5 minutos y usa webhook-id para descartar duplicados. Responde 2xx en cuanto la guardes y procésala después.

AllSign le hace POST a la URL que registraste, con el evento document.created. Responde 2xx rápido y, antes de confiar en el cuerpo, verifica la firma: webhook-signature es un HMAC-SHA256 de webhook-id.webhook-timestamp.cuerpo con el secreto del webhook (Webhooks).

Esta entrega llegó de verdad al receptor del test y su firma se verificó con el secreto del webhook.

Qué mirar

  • eventType = "document.created"
  • data.documentId = "doc_8a9708721fb54fcfabc32715b87f5518"

En producción

Igual con una key live. La cabecera allsign-livemode y el campo livemode te dicen si el evento es de producción o de sandbox.

5 Registra un endpoint que falla

POST /webhooks ver en la referencia

Qué haces

Para ver qué pasa cuando tu servidor falla, este segundo endpoint de pruebas responde 500 a propósito.

Qué mirar

  • id = "whe_67554d50996d47b9b68963f6cb2fffac"

6 Revisa las entregas que fallaron

GET /webhooks/{webhook_id}/deliveries ver en la referencia

Qué haces

Cada entrega guarda sus intentos en attemptHistory, con el status que respondió tu servidor. Un 5xx, un timeout o un error de red se reintentan con espera creciente durante 3 días, y mientras tanto la entrega sigue en PENDING; un 4xx la deja en FAILED de inmediato, porque AllSign entiende que tu servidor la rechazó a propósito. Tu endpoint recibe los eventos de toda tu cuenta, no solo del documento que acabas de crear, así que esta lista también puede traer entregas de otros documentos. Es lo primero que revisas cuando «no llegan los webhooks».

Qué mirar

  • data.0.status = "PENDING"
  • data.0.attempts = 1
  • data.0.attemptHistory.0.statusCode = 500
  • data.0.attemptHistory.0.outcome = "TRANSIENT"

7 Rota el secreto

POST /webhooks/{webhook_id}/rotate-secret ver en la referencia

Qué haces

Rota en cuanto sospeches que el secreto se filtró. La respuesta trae el secreto nuevo una sola vez; el anterior sigue valiendo 24 horas para que despliegues el cambio sin perder entregas.

Qué mirar

  • secret = "whsec_…"

8 Reintenta la rotación con la misma Idempotency-Key

POST /webhooks/{webhook_id}/rotate-secret ver en la referencia

Reúsa la Idempotency-Key del paso 7: córrelo justo después de ese paso, en la misma terminal o el mismo script.

Qué haces

Si se cae la red y reintentas con la misma llave, no rota dos veces: recibes el mismo secreto y la cabecera Idempotency-Replayed: true.

Qué mirar

  • secret = "whsec_…"

9 Verifica una entrega con el secreto nuevo

POST tu endpoint · evento document.created

Qué haces

Durante esas 24 horas webhook-signature trae dos firmas separadas por espacio, una por secreto. Acepta la entrega si cualquiera cuadra con el secreto que tienes.

AllSign le hace POST a la URL que registraste, con el evento document.created. Responde 2xx rápido y, antes de confiar en el cuerpo, verifica la firma: webhook-signature es un HMAC-SHA256 de webhook-id.webhook-timestamp.cuerpo con el secreto del webhook (Webhooks).

Esta entrega llegó de verdad al receptor del test y su firma se verificó con el secreto del webhook.

Qué mirar

  • data.documentId = "doc_47e66108a3c64e1385ff314c388b7d6b"

10 Borra el endpoint cuando ya no lo uses

DELETE /webhooks/{webhook_id} ver en la referencia

Qué haces

Borrarlo detiene las entregas de inmediato; si solo quieres pausarlo, usa PATCH con disabled: true.

Errores comunes de esta receta

Errores reales que el test de esta receta provocó a propósito en sandbox, sobre sus mismos pasos. Cada uno enlaza a su explicación en Errores.

WEBHOOK_URL_INVALID · 422 · en el paso 2 (Registra tu endpoint)

Registra un endpoint con http:// en vez de https://. Qué significa y qué hacer

Respuesta · 422 · WEBHOOK_URL_INVALID
{
  "type": "https://allsign.io/developers/docs/errors#WEBHOOK_URL_INVALID",
  "title": "Unprocessable Entity",
  "status": 422,
  "detail": "The webhook url must be an absolute https:// URL.",
  "instance": "/v3/webhooks",
  "code": "WEBHOOK_URL_INVALID",
  "requestId": "req_b2c680669f144fcc806cb8366bae5594",
  "errors": [
    {
      "field": "url",
      "code": "INVALID_VALUE",
      "detail": "Must start with https://"
    }
  ]
}

Grabado en el entorno interno de AllSign con una key de sandbox · backend 24c0469 · · test docs-checks-v3/tests/recetas/recibe-y-verifica-webhooks.receta.spec.ts

UNKNOWN_EVENT_TYPE · 422 · en el paso 2 (Registra tu endpoint)

Suscríbete a un evento que no existe. Qué significa y qué hacer

Respuesta · 422 · UNKNOWN_EVENT_TYPE
{
  "type": "https://allsign.io/developers/docs/errors#UNKNOWN_EVENT_TYPE",
  "title": "Unprocessable Entity",
  "status": 422,
  "detail": "One or more event types are not in the v3 catalog.",
  "instance": "/v3/webhooks",
  "code": "UNKNOWN_EVENT_TYPE",
  "requestId": "req_45571c3852cb4ac6bae34f64dc886a28",
  "errors": [
    {
      "field": "events[0]",
      "code": "UNKNOWN_EVENT_TYPE",
      "detail": "'documento.firmado' is not a valid v3 event type."
    }
  ]
}

Grabado en el entorno interno de AllSign con una key de sandbox · backend 24c0469 · · test docs-checks-v3/tests/recetas/recibe-y-verifica-webhooks.receta.spec.ts

WEBHOOK_NOT_FOUND · 404 · en GET /v3/webhooks/whe_54c1d95de8c64a369095bc044665b5b3

Consulta un endpoint que ya borraste. Qué significa y qué hacer

Respuesta · 404 · WEBHOOK_NOT_FOUND
{
  "type": "https://allsign.io/developers/docs/errors#WEBHOOK_NOT_FOUND",
  "title": "Not Found",
  "status": 404,
  "detail": "No webhook endpoint was found with that id.",
  "instance": "/v3/webhooks/whe_54c1d95de8c64a369095bc044665b5b3",
  "code": "WEBHOOK_NOT_FOUND",
  "requestId": "req_e424fdd005fc4298b0231312f2126597"
}

Grabado en el entorno interno de AllSign con una key de sandbox · backend 24c0469 · · test docs-checks-v3/tests/recetas/recibe-y-verifica-webhooks.receta.spec.ts

Siguiente