Devs
February 10, 2026

Webhooks para Facturación CFDI: Guía Técnica de Eventos en Tiempo Real

Cuando integras facturación CFDI en tu aplicación, necesitas saber cuándo ocurren eventos importantes: una factura se timbró exitosamente, una cancelación fue aceptada, un complemento de pago se generó. Los webhooks son la solución elegante para recibir estas notificaciones en tiempo real.

En esta guía técnica te explicamos cómo implementar webhooks con la API de gigstack, qué eventos están disponibles y las mejores prácticas para construir integraciones robustas.

¿Qué son los Webhooks y por qué usarlos?

Un webhook es una llamada HTTP que un servidor hace a tu aplicación cuando ocurre un evento específico. En lugar de hacer polling constante ("¿ya se timbró?", "¿ya se canceló?"), configuras una URL y gigstack te notifica automáticamente.

Ventajas vs polling:

  • Tiempo real: Recibes la notificación segundos después del evento
  • Eficiencia: Sin requests innecesarios a la API
  • Escalabilidad: Funciona igual con 10 o 10,000 facturas diarias
  • Desacoplamiento: Tu aplicación reacciona a eventos sin estar acoplada al proceso de timbrado

Eventos disponibles en gigstack API

La API de gigstack emite webhooks para los siguientes eventos:

Eventos de facturas (invoices)

  • invoice.created: Factura timbrada exitosamente. Incluye UUID, XML, PDF y datos completos del CFDI.
  • invoice.cancelled: Factura cancelada exitosamente ante el SAT.
  • invoice.cancellation_requested: Se solicitó cancelación, esperando respuesta del receptor.
  • invoice.cancellation_accepted: El receptor aceptó la cancelación.
  • invoice.cancellation_rejected: El receptor rechazó la cancelación.
  • invoice.error: Error al intentar timbrar (datos inválidos, error de PAC, etc.).

Eventos de pagos (payments)

  • payment.received: Pago registrado en el sistema.
  • payment.complement_created: Complemento de pago (CFDI) generado exitosamente.
  • payment.matched: Pago conciliado con factura existente.

Eventos de clientes (clients)

  • client.created: Nuevo cliente con datos fiscales validados.
  • client.updated: Datos fiscales del cliente actualizados.
  • client.validation_failed: Datos fiscales no pasaron validación del SAT.

Configurar tu endpoint de webhook

Paso 1: Crea el endpoint en tu servidor

Tu endpoint debe aceptar requests POST con body JSON. Ejemplo en Node.js/Express:

const express = require('express'); const app = express(); app.use(express.json()); app.post('/webhooks/gigstack', (req, res) => { const event = req.body; console.log('Evento recibido:', event.type); console.log('Datos:', event.data); // Procesa el evento según su tipo switch(event.type) { case 'invoice.created': handleInvoiceCreated(event.data); break; case 'invoice.cancelled': handleInvoiceCancelled(event.data); break; case 'payment.complement_created': handleComplementCreated(event.data); break; } // Responde 200 para confirmar recepción res.status(200).json({ received: true }); }); app.listen(3000);

Paso 2: Registra el webhook en gigstack

Desde el panel de gigstack o via API:

POST https://api.gigstack.io/v2/webhooks Authorization: Bearer tu_jwt_token Content-Type: application/json { "url": "https://tu-dominio.com/webhooks/gigstack", "events": ["invoice.created", "invoice.cancelled", "payment.complement_created"], "active": true }

Paso 3: Verifica la conexión

gigstack envía un evento de prueba al registrar el webhook. Verifica en tus logs que llegó correctamente.

Estructura del payload

Todos los webhooks tienen esta estructura base:

{ "id": "evt_abc123", "type": "invoice.created", "created_at": "2026-02-10T15:30:00Z", "data": { // Datos específicos del evento } }

Ejemplo: invoice.created

{ "id": "evt_inv_xyz789", "type": "invoice.created", "created_at": "2026-02-10T15:30:00Z", "data": { "invoice_id": "inv_abc123", "uuid": "A1B2C3D4-E5F6-7890-ABCD-EF1234567890", "folio": "F-001234", "serie": "A", "total": 1160.00, "subtotal": 1000.00, "currency": "MXN", "client": { "id": "cli_def456", "name": "Empresa SA de CV", "rfc": "EMP123456ABC" }, "status": "valid", "xml_url": "https://storage.gigstack.io/invoices/xyz/invoice.xml", "pdf_url": "https://storage.gigstack.io/invoices/xyz/invoice.pdf", "stamped_at": "2026-02-10T15:30:00Z" } }

Ejemplo: invoice.cancelled

{ "id": "evt_can_abc456", "type": "invoice.cancelled", "created_at": "2026-02-10T16:45:00Z", "data": { "invoice_id": "inv_abc123", "uuid": "A1B2C3D4-E5F6-7890-ABCD-EF1234567890", "cancellation_reason": "02", "substitute_uuid": "B2C3D4E5-F6A7-8901-BCDE-F12345678901", "cancelled_at": "2026-02-10T16:45:00Z" } }

Mejores prácticas de implementación

1. Responde rápido (< 5 segundos)

gigstack espera una respuesta 2xx dentro de 5 segundos. Si tu procesamiento es largo, responde inmediatamente y procesa en background:

app.post('/webhooks/gigstack', async (req, res) => { // Responde inmediatamente res.status(200).json({ received: true }); // Procesa en background processEventAsync(req.body); });

2. Implementa idempotencia

Los webhooks pueden enviarse más de una vez (reintentos por timeout, etc.). Usa el event.id para detectar duplicados:

const processedEvents = new Set(); function handleWebhook(event) { if (processedEvents.has(event.id)) { console.log('Evento duplicado, ignorando:', event.id); return; } processedEvents.add(event.id); // Procesa el evento... }

En producción, usa Redis o tu base de datos para persistir los IDs procesados.

3. Verifica la firma del webhook

gigstack firma cada webhook con un secret. Verifica la firma para asegurar que el request es auténtico:

const crypto = require('crypto'); function verifyWebhookSignature(payload, signature, secret) { const expectedSignature = crypto .createHmac('sha256', secret) .update(JSON.stringify(payload)) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expectedSignature) ); } app.post('/webhooks/gigstack', (req, res) => { const signature = req.headers['x-gigstack-signature']; if (!verifyWebhookSignature(req.body, signature, WEBHOOK_SECRET)) { return res.status(401).json({ error: 'Invalid signature' }); } // Procesa el evento... });

4. Implementa cola de reintentos

Si tu procesamiento falla, guarda el evento en una cola para reintentarlo después:

async function handleWebhook(event) { try { await processEvent(event); } catch (error) { console.error('Error procesando evento:', error); await saveToRetryQueue(event); } }

5. Logging detallado

Guarda logs de todos los webhooks recibidos. Te ayudará a debuggear y auditar:

app.post('/webhooks/gigstack', async (req, res) => { const event = req.body; // Log completo await db.webhookLogs.create({ event_id: event.id, event_type: event.type, payload: JSON.stringify(event), received_at: new Date(), ip_address: req.ip }); // Procesa... });

Casos de uso prácticos

1. Notificar al cliente cuando su factura está lista

async function handleInvoiceCreated(data) { const { client, pdf_url, uuid } = data; await sendEmail({ to: client.email, subject: 'Tu factura está lista', body: 'Descarga tu CFDI: ' + pdf_url, attachments: [{ url: pdf_url }] }); console.log('Email enviado para factura:', uuid); }

2. Actualizar tu base de datos local

async function handleInvoiceCreated(data) { await db.invoices.updateOne( { internal_id: data.invoice_id }, { $set: { uuid: data.uuid, status: 'stamped', xml_url: data.xml_url, pdf_url: data.pdf_url, stamped_at: data.stamped_at } } ); }

3. Sincronizar con tu sistema contable

async function handleInvoiceCreated(data) { // Crea el asiento contable await accountingSystem.createEntry({ date: data.stamped_at, description: 'Factura ' + data.folio, debit: { account: 'cuentas_por_cobrar', amount: data.total }, credit: { account: 'ingresos', amount: data.subtotal }, credit: { account: 'iva_trasladado', amount: data.total - data.subtotal } }); }

4. Alertar sobre cancelaciones

async function handleInvoiceCancelled(data) { // Notifica al equipo de finanzas await slack.sendMessage({ channel: '#finanzas', text: '⚠️ Factura cancelada: ' + data.uuid + '\nMotivo: ' + data.cancellation_reason }); // Actualiza tu sistema await db.invoices.updateOne( { uuid: data.uuid }, { $set: { status: 'cancelled', cancelled_at: data.cancelled_at } } ); }

Debugging y monitoreo

Panel de webhooks en gigstack

Desde app.gigstack.pro puedes ver:

  • Historial de webhooks enviados
  • Status de entrega (exitoso, fallido, pendiente)
  • Payload completo de cada evento
  • Opción de reenviar webhooks fallidos

Herramientas para desarrollo local

Para probar webhooks en localhost, usa ngrok o similar:

ngrok http 3000 # Obtienes una URL como https://abc123.ngrok.io # Úsala como webhook URL en gigstack

Preguntas frecuentes

¿Qué pasa si mi servidor está caído?

gigstack reintenta webhooks fallidos con backoff exponencial: 1 min, 5 min, 30 min, 2 hrs, 12 hrs. Después de 24 horas sin éxito, el webhook se marca como fallido pero puedes reenviarlo manualmente.

¿Puedo filtrar qué eventos recibo?

Sí. Al crear el webhook, especificas qué eventos quieres recibir. No recibirás eventos no suscritos.

¿Hay límite de webhooks por cuenta?

Puedes configurar hasta 10 webhooks activos por cuenta. Si necesitas más, contacta soporte.

¿Los webhooks incluyen el XML completo?

El payload incluye URLs al XML y PDF, no el contenido directamente. Esto mantiene el payload ligero. Descarga los archivos desde las URLs proporcionadas.

Implementa webhooks hoy

Los webhooks son esenciales para construir integraciones de facturación robustas y en tiempo real. Con gigstack, recibes notificaciones instantáneas de todos los eventos importantes sin polling ni complejidad.

Consulta la documentación completa en docs.gigstack.io y empieza a integrar webhooks en tu aplicación.

Blogs que te pueden gustar