Webhooks
Cómo Timbre te avisa, y cómo verificar que fue Timbre.
Cuando Hacienda resuelve, Timbre llama a tu servidor. Es la forma correcta de enterarte: no dependés de consultar y no perdés el veredicto si tu proceso estaba caído —la entrega se reintenta.
Todo lo de esta página —registrar un endpoint, probarlo, rotar el secreto, darlo de baja, ver las entregas— también se hace desde el panel, en Integración → Webhooks, sin escribir una sola petición. Lo de abajo es para cuando lo vas a manejar por código.
Registrar un endpoint
curl -X POST https://api.timbre.cr/internal/v1/webhooks/endpoints \
-H "Authorization: Bearer timbre_sk_…" \
-H 'Content-Type: application/json' \
-d '{
"url": "https://tu-servidor.cr/timbre"
}'La URL debe ser https:. Un aviso lleva la clave y la identificación del
contribuyente, y eso no viaja en claro.
La respuesta trae el secreto, una sola vez. Guardalo al recibirlo.
Verificar la firma
Cada entrega llega con tres encabezados:
Timbre-Delivery-Id: …
Timbre-Event-Type: invoice.accepted
Timbre-Signature: t=1712345678,v1=…La firma es HMAC-SHA256 sobre esta cadena exacta:
v1:{deliveryId}:{t}:{cuerpo crudo}Tres cosas importan al verificarla. Usá el cuerpo crudo, tal cual llegó —si
lo parseás y lo volvés a serializar, la firma no va a coincidir. Compará en
tiempo constante, no con ==. Y rechazá lo que tenga t viejo; 300
segundos de tolerancia es razonable.
Sin lo último, cualquiera que haya capturado una entrega válida puede reproducirla mañana.
Timbre-Signature puede traer más de un v1 —t=…,v1=abc…,v1=def…— mientras
estés en la ventana de solapamiento de una rotación. Aceptá la entrega si
cualquiera de los v1 coincide con tu secreto; no asumas que es uno solo
ni que el primero es el vigente.
Los eventos
| Evento | Cuándo |
|---|---|
invoice.finalized | Numerado y encaminado |
invoice.processing | Hacienda lo recibió |
invoice.accepted | Aceptado |
invoice.rejected | Rechazado |
invoice.permanent_error | Falló definitivamente |
webhook.test | Una prueba que pediste vos |
Probar sin emitir
curl -X POST https://api.timbre.cr/internal/v1/webhooks/endpoints/{id}/test \
-H "Authorization: Bearer timbre_sk_…"Manda un webhook.test real, firmado igual que los demás, para que verifiques
tu verificación antes de que haya un documento de por medio.
Rotar el secreto
curl -X POST https://api.timbre.cr/internal/v1/webhooks/endpoints/{id}/secrets \
-H "Authorization: Bearer timbre_sk_…"El anterior sigue siendo válido durante un solapamiento, para que puedas
desplegar el nuevo sin perder entregas en el medio. Cuánto dura lo elegís vos
con ?overlapSeconds=; sin ese parámetro usa el valor por defecto del
servidor, y 0 es corte inmediato —el secreto anterior deja de firmar ya.
Administrar tus endpoints
curl https://api.timbre.cr/internal/v1/webhooks/endpoints \
-H "Authorization: Bearer timbre_sk_…"Lista los endpoints del proyecto —nunca con el secreto, que solo se ve al crearlos o al rotarlos. Para dar de baja uno:
curl -X DELETE https://api.timbre.cr/internal/v1/webhooks/endpoints/{id} \
-H "Authorization: Bearer timbre_sk_…"Deja de recibir eventos nuevos. Las entregas que ya existían quedan como evidencia y no se reintentan mientras esté deshabilitado.
Ver qué se entregó
curl https://api.timbre.cr/internal/v1/webhooks/deliveries \
-H "Authorization: Bearer timbre_sk_…"Cada entrega queda con su estado —pending, delivered o failed— y la
evidencia de cada intento.