Webhooks
Un webhook es una URL de tu servidor a la que Facturear le manda unPOST cada vez que pasa algo en tu cuenta: ARCA autorizó una factura, una factura falló, terminó un lote, llegó una comunicación de ARCA. Así tu sistema se entera en el momento, sin tener que consultar la API cada tanto.
Crear y administrar webhooks
Tenés dos caminos:- Desde el panel: en facture.ar/app/webhooks tocá Agregar webhook, poné la URL, elegí los eventos y, si querés, headers propios. Desde la lista podés pausarlo o activarlo, editarlo, eliminarlo, ver el Secreto de firma y las Entregas.
- Desde la API:
La URL tiene que ser
https y con una dirección pública (no se aceptan localhost ni IPs privadas).
Los webhooks son configuración de la cuenta y los comparten los dos entornos: cada evento dice en environment si viene de homologación o de producción. Por eso, para crearlos, editarlos o borrarlos por API hace falta la key de producción: con una key de testing, esos pedidos responden 403 con code: "TEST_KEY_READ_ONLY" (listarlos sí se puede).
La respuesta del
POST trae el secreto de firma (whsec_...). Es la única vez que la API lo devuelve: guardalo. Después lo ves en el panel, en Secreto de firma.Eventos
Payload
Cada envío es unPOST con Content-Type: application/json. El body siempre trae estos campos, más los del evento:
invoice.completed
invoice.failed
Los mismos campos de la factura que eninvoice.completed, sin cae ni caeFchVto (cualquiera puede venir en null, por ejemplo invoice.id si la factura falló antes de crearse), más el motivo:
errors son los errores de ARCA y observations, las observaciones del comprobante; cada uno con code (puede ser null) y message. Si la factura falló antes de llegar a ARCA, errors trae un solo mensaje sin código.
batch.completed
GET /api/invoices/batches/{id}.
draft.review_required
Trae el borrador completo endraft, con los mismos campos que GET /api/documents/drafts/{id}:
dfe.message.received
urgency es info, accion_requerida o critico. summary, action y deadline los completa una lectura con IA y pueden venir en null. Cada sincronización manda como mucho 50 de estos eventos por CUIT.
tax_account.updated
debt y credit son el saldo deudor y acreedor en pesos; previousDebt es el saldo deudor de la lectura anterior (null si es la primera). En cada línea, side es deudor o acreedor, y period, concept y dueDate pueden faltar.
Headers
Además van los headers propios que configuraste en el webhook. No pueden reemplazar
Facturear-Signature, Facturear-Event ni Facturear-Delivery.
Verificar la firma
Verificá cada envío antes de hacerle caso. Los pasos:- Leé el body crudo, tal cual llegó, sin parsearlo.
- Del header
Facturear-Signature, sacáty losv1(separados por coma). - Calculá el HMAC-SHA256 en hex de
"<t>.<body crudo>"con tu secretowhsec_...(el secreto completo, con el prefijo). - Comparalo con cada
v1en tiempo constante. - Rechazá el envío si
testá a más de 300 segundos de tu reloj: así nadie puede reenviarte una entrega vieja.
Node.js (Express)
Python (Flask)
Errores comunes
- Parsear el JSON antes de verificar. Si tu framework ya parseó el body y lo volvés a serializar, cambian espacios u orden de los campos y la firma no coincide. Verificá sobre los bytes que llegaron (
express.raw,request.get_data(),await request.body()en FastAPI). - Usar el secreto sin el prefijo. El secreto es el texto completo,
whsec_incluido. - Comparar con
==. Usá una comparación en tiempo constante (crypto.timingSafeEqual,hmac.compare_digest). - Reloj del servidor corrido. Con más de 300 segundos de diferencia, las firmas válidas se rechazan. Sincronizá la hora con NTP.
- Usar un secreto viejo. Si rotaste el secreto, el anterior deja de servir en el acto.
Entregas y reintentos
- Éxito: cualquier respuesta
2xxdentro de los 10 segundos. Un3xxcuenta como falla: las redirecciones no se siguen, así que usá la URL final. - Reintentos automáticos: si tu servidor no responde a tiempo, falla la conexión o responde algo que no es
2xx, se reintenta enseguida (a 1 y 2 segundos), hasta 3 intentos en total. Cada intento se firma con su propiot; elFacturear-Deliveryy el body son los mismos. - Después de 3 intentos la entrega queda como fallida y no se reintenta sola.
- Entregas en el panel: en facture.ar/app/webhooks, Ver entregas muestra cada envío con su evento, estado, código de respuesta, el payload y los headers que mandamos, y el principio del body que respondiste. Con Reintentar volvés a mandar una entrega: va con el mismo body (mismo
idde evento), la URL, los headers y el secreto actuales del webhook, y unFacturear-Deliverynuevo. - Sin duplicados de nuestro lado: cada evento se arma una sola vez (por ejemplo, un
invoice.completedpor factura). Igual pueden llegarte repetidos por los reintentos o por un reintento manual, así que deduplicá por eliddel body.
Secreto de firma
- Se genera al crear el webhook:
whsec_y 48 caracteres hexadecimales. - La API lo devuelve solo en la respuesta del
POST /api/webhooks.GETyPUTno lo incluyen. - En el panel lo ves con Secreto de firma, y con Rotar secreto generás uno nuevo. La rotación es inmediata: el secreto anterior deja de verificar en el acto, así que actualizalo en tu servidor enseguida.