Webhooks

Facturalo puede notificar a tu servidor en tiempo real cuando ocurre un evento importante. Configura una URL por entorno y tu servidor recibirá un POST con el payload del evento.

¿Qué eventos envía Facturalo via webhook?

EventoDescripciónActivo por defecto
documento.sunat.respuestaSUNAT procesó el documento (aceptado o rechazado).Activo
documento.anuladoLa baja del documento fue aceptada por SUNAT.Activo
documento.correo.enviadoEl PDF fue enviado al correo del cliente.Inactivo
documento.correo.fallidoEl envío del correo al cliente falló.Inactivo
guia.sunat.respuestaSUNAT procesó la guía de remisión.Activo
documento.errorError interno al generar o enviar el documento.Activo

¿Cómo verificar la firma HMAC de un webhook?

Cada evento incluye el header X-Facturalo-Firma con un HMAC-SHA256 del body usando tu webhookSecret. Verifica siempre la firma antes de procesar el evento.

X-Facturalo-Firma: {hmac-sha256}
Verificación y procesamiento — PHPphp
<?php
// En tu endpoint POST /webhooks/facturalo

$rawBody      = file_get_contents('php://input');
$firmaRecibida = $_SERVER['HTTP_X_FACTURALO_FIRMA'] ?? '';
$webhookSecret = 'whsec_test_xxxxxxxxxxxxxxxxxxxx'; // tu webhookSecret

// Verificar firma
$firmaEsperada = hash_hmac('sha256', $rawBody, $webhookSecret);
if (!hash_equals($firmaEsperada, $firmaRecibida)) {
    http_response_code(401);
    exit('Firma inválida');
}

// Procesar evento
$payload = json_decode($rawBody, true);

switch ($payload['evento']) {
    case 'documento.sunat.respuesta':
        $doc    = $payload['documento'];
        $estado = $doc['estado']; // ACEPTADA | RECHAZADA
        // guardar estado en tu BD...
        break;

    case 'documento.anulado':
        // marcar como anulado en tu BD...
        break;

    case 'documento.error':
        $error = $payload['error']['mensaje'];
        // registrar error...
        break;
}

http_response_code(200);
echo json_encode(['ok' => true]);

¿Qué pasa si mi servidor no responde el webhook?

Si tu servidor no responde con un código 2xx, el evento queda marcado como fallido y puedes reenviarlo manualmente desde Listar webhooks enviados. El payload reenviado es exactamente el original guardado en base de datos.