Webhooks en Tiempo Real
Notificaciones asíncronas HTTP con firma HMAC-SHA256.
Los webhooks eliminan la necesidad de hacer polling a la API. Recibirás un evento HTTP POST automático cada vez que SUNAT confirme la aprobación o rechazo de un comprobante.
Verificación de Firma HMAC
Cada petición HTTP contiene la cabecera X-YoEmito-Signature: t={timestamp},v1={hmac}, calculada sobre el body crudo exacto que enviamos (antes de cualquier parseo). Verificarla contra el JSON re-serializado por tu framework casi siempre falla, porque el orden de las claves o los espacios pueden cambiar.
Con Node.js, usa verifyWebhookSignature del SDK oficial en vez de reimplementar el HMAC a mano:
import express from "express";
import { verifyWebhookSignature } from "@yoemito/sdk-node";
// express.raw() conserva el body EXACTO (Buffer), no lo parsees antes
app.post(
"/webhook-receiver",
express.raw({ type: "application/json" }),
(req, res) => {
const rawBody = req.body.toString("utf8");
const isValid = verifyWebhookSignature(
process.env.YOEMITO_WEBHOOK_SECRET,
req.headers["x-yoemito-signature"],
rawBody,
);
if (!isValid) return res.status(401).send("Firma inválida");
const event = JSON.parse(rawBody);
// ... procesa event.type / event.data
res.sendStatus(200);
},
);Sin la SDK (otro lenguaje, u otro runtime), replica la misma lógica: HMAC-SHA256(secret, "{t}.{rawBody}") en hexadecimal, comparado con v1 mediante comparación de tiempo constante, rechazando timestamps con más de 300s de antigüedad.
En Fastify o Next.js aplica el mismo principio: captura el body como texto/Buffer crudo antes de que el framework lo parsee a JSON automáticamente.
Catálogo de Eventos
Llama a GET /v1/webhooks/events para obtener la lista completa de eventos disponibles junto con un ejemplo real de payload por cada uno — útil para saber qué suscribir sin tener que provocar cada evento manualmente en Sandbox.
| Evento | Cuándo se dispara |
|---|---|
| document.accepted | SUNAT aceptó el comprobante (factura/boleta/nota). |
| document.accepted_with_warnings | Aceptado con observaciones no bloqueantes. |
| document.rejected | SUNAT rechazó el comprobante. |
| document.cdr_ready | La Constancia de Recepción ya está disponible para descarga. |
| document.queued_sunat_down | SUNAT no respondió; se reintentará automáticamente. |
| document.status_changed | Genérico: se emite en toda transición, además del evento específico. |
| voided.accepted / .rejected / .status_changed | Equivalentes para Comunicación de Baja (RA). |
| summary.accepted / .rejected / .status_changed | Equivalentes para Resumen Diario de Boletas (RC). |
| certificate.expiring | El certificado digital de un emisor vencerá pronto. |
| api_key.created / .revoked / .deleted / .rotated | Actividad sobre las API Keys de la organización. |
Historial de Entregas
GET /v1/webhooks/:id/deliveries devuelve el historial de intentos de un webhook (evento, número de intento, código HTTP recibido y error) — también visible desde el panel en Webhooks → Ver entregas. Por privacidad, el payload y la URL de la entrega se purgan una vez resuelta (entregada o agotados los reintentos); el historial retiene solo los metadatos necesarios para diagnosticar un fallo.