Webhooks

Recibe notificaciones en tiempo real cuando la DIAN procesa tus documentos.

Crear un webhook

Registra una URL donde recibirás eventos POST con el resultado de tus documentos.

curl -X POST https://api.mercalo.co/v1/webhooks \
  -H "Authorization: Bearer mk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://mi-app.com/api/webhooks/mercalo",
    "events": [
      "document.accepted",
      "document.rejected",
      "document.error"
    ],
    "secret": "mi-secreto-seguro"
  }'

Eventos disponibles

EventoDescripción
document.acceptedDocumento (factura, nota crédito/débito, doc. soporte, etc.) aceptado por la DIAN
document.rejectedDocumento rechazado por la DIAN — revisa dianResponse para el detalle
document.errorError al procesar el documento (no llegó a ser evaluado por la DIAN)

Payload de ejemplo

Cada webhook envía un POST con el siguiente formato JSON:

{
  "documentId": "5f2c7a1e-...",
  "number": "SETP990000001",
  "cufe": "a1b2c3d4...",
  "trackId": "b7e91c...",
  "dianStatus": "ACCEPTED",
  "type": "invoice"
}

Verificar firma

Si configuraste un secret al crear el webhook, cada request incluye el header X-Webhook-Signature con un HMAC-SHA256 del cuerpo del request. Sin secret configurado, este header no se envía. Siempre verifica la firma antes de procesar si dependes de la autenticidad del evento.

También recibes X-Webhook-Event (el mismo valor que el campo type del payload) y X-Webhook-Id con el identificador del webhook que disparó el evento.

import crypto from 'crypto';

function verifyWebhookSignature(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// En tu handler
app.post('/api/webhooks/mercalo', (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  if (!verifyWebhookSignature(req.rawBody, signature, 'mi-secreto-seguro')) {
    return res.status(401).send('Invalid signature');
  }
  // Procesar evento...
  res.status(200).send('OK');
});

Reintentos

Si tu endpoint no responde con 2xx en 10 segundos (timeout), o responde con un error, reintentamos automáticamente con backoff exponencial — hasta 5 intentos en total, con la espera entre intentos creciendo en segundos, no en horas.

Puedes ver el historial de entregas (código HTTP, éxito/fallo, duración) de cada webhook desde el panel, y disparar un evento de prueba en cualquier momento sin esperar a un documento real.