Skip to Content
Webhooks

Webhooks

Los webhooks de FactuLink te permiten recibir notificaciones HTTP en tiempo real cuando ocurren eventos importantes en tu cuenta, como el timbrado o cancelacion de un CFDI.

En lugar de hacer polling constante a la API, configuras una URL en tu servidor y FactuLink envia un POST con el detalle del evento cada vez que algo sucede.

Disponible en produccion — base URL https://api.factulink.com.mx. Requiere una API Key (sk_test_... o sk_live_...) o un JWT con rol ADMIN.


Eventos disponibles

EventoEstadoDescripcion
cfdi.timbradoVivoEl CFDI fue timbrado exitosamente por el PAC
cfdi.canceladoVivoEl CFDI fue cancelado ante el SAT
cfdi.fallidoVivoEl timbrado de un CFDI fallo (error de PAC o validacion)
cfdi.encoladoVivoEl CFDI fue encolado para timbrado asincrono
csd.por_vencerPlaneadoUn CSD esta proximo a vencer (30 dias antes)
csd.vencidoPlaneadoUn CSD ha vencido y ya no puede firmar CFDIs

Registrar un webhook

curl -X POST https://api.factulink.com.mx/api/v1/webhooks \ -H "Authorization: Bearer sk_test_..." \ -H "Content-Type: application/json" \ -d '{ "url": "https://tu-servidor.com/fc-webhook", "events": ["cfdi.timbrado", "cfdi.cancelado"] }'

La respuesta incluye el secret una sola vez — guardalo de inmediato, no se puede recuperar despues:

{ "id": "9c2b1f3e-4d5a-6b7c-8d9e-0f1a2b3c4d5e", "url": "https://tu-servidor.com/fc-webhook", "events": ["cfdi.timbrado", "cfdi.cancelado"], "secret": "whsec_3f8b...", "estado": "ACTIVO" }

Endpoints disponibles

MetodoRutaDescripcion
POST/api/v1/webhooksCrear webhook (devuelve secret una sola vez)
GET/api/v1/webhooksListar webhooks del tenant
PATCH/api/v1/webhooks/:idActualizar url, events o estado
DELETE/api/v1/webhooks/:idEliminar webhook
GET/api/v1/webhooks/:id/deliveriesHistorial de entregas
POST/api/v1/webhooks/:id/testDisparar evento de prueba

Formato del payload

Cada webhook envia un POST con un payload JSON con la siguiente estructura:

{ "event": "cfdi.timbrado", "tenant_id": "01HQ3KD5R8N2YPTM4VBWG7E9CK", "created_at": "2026-04-07T10:35:12.000Z", "data": { "cfdi_id": "f1e2d3c4-b5a6-7980-1234-567890abcdef", "uuid": "6128396f-c09c-4e3a-b4d7-8a5f2e1c9b3d" } }

El contenido de data varia por tipo de evento:

EventoCampos en data
cfdi.timbradocfdi_id, uuid
cfdi.canceladocfdi_id, uuid, motivo, estado
cfdi.fallidocfdi_id
cfdi.encoladocfdi_id

Headers enviados

Cada request incluye los siguientes headers:

HeaderValor
Content-Typeapplication/json
X-FC-EventEl nombre del evento (ej. cfdi.timbrado)
X-FC-Signaturesha256=<hex> HMAC-SHA256 del body usando tu webhook secret
X-FC-Delivery-IdUUID unico de la entrega (idempotencia)
X-FC-TimestampUnix timestamp en segundos

Verificacion de firma

X-FC-Signature contiene una firma HMAC-SHA256 del body crudo usando tu webhook secret. Verificala antes de confiar en el payload:

import crypto from 'node:crypto'; function verifySignature(rawBody, signatureHeader, secret) { const expected = crypto .createHmac('sha256', secret) .update(rawBody, 'utf-8') .digest('hex'); const expectedHeader = `sha256=${expected}`; return crypto.timingSafeEqual( Buffer.from(signatureHeader), Buffer.from(expectedHeader), ); }

Importante: Usa el body crudo (raw bytes) tal como llego en el request, no el JSON re-serializado. Cualquier cambio de espacios o orden de claves rompe la firma.


Reintentos y entrega

  • Timeout: 10 segundos por intento.
  • Exito: cualquier respuesta 2xx marca la entrega como exitosa.
  • Reintentos: las entregas fallidas se reintentan automáticamente con backoff exponencial.
  • Historial: consulta GET /api/v1/webhooks/:id/deliveries para ver el estado, status code y body de respuesta de cada entrega.
  • Auto-pausa: si un endpoint acumula 10 fallas consecutivas (campo consecutive_failures), se pausa automaticamente. Reactivalo con PATCH /api/v1/webhooks/:id cambiando estado a ACTIVO.
  • Idempotencia: trata cada X-FC-Delivery-Id como único — si recibes el mismo delivery dos veces (por reintento), procesa el evento una sola vez.

Probar tu integracion

Usa el endpoint de prueba para disparar un evento sintetico sin esperar a que ocurra uno real:

curl -X POST https://api.factulink.com.mx/api/v1/webhooks/<webhook-id>/test \ -H "Authorization: Bearer sk_test_..."

SDK Node.js

Si usas @factulink/node, los webhooks se administran con el resource fc.webhooks:

import FactuLink from '@factulink/node'; const fc = new FactuLink({ apiKey: process.env.FC_API_KEY! }); const { data: webhook } = await fc.webhooks.create({ url: 'https://tu-servidor.com/fc-webhook', events: ['cfdi.timbrado', 'cfdi.cancelado'], }); console.log('Guarda este secret:', webhook.secret);

Consulta la referencia del SDK Node.js para mas detalles.

Last updated on