Skip to main content

Webhooks

Un webhook es una URL de tu servidor a la que Facturear le manda un POST 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 un POST 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 en invoice.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

Para ver el resultado de cada factura del lote, consultá GET /api/invoices/batches/{id}.

draft.review_required

Trae el borrador completo en draft, 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:
  1. Leé el body crudo, tal cual llegó, sin parsearlo.
  2. Del header Facturear-Signature, sacá t y los v1 (separados por coma).
  3. Calculá el HMAC-SHA256 en hex de "<t>.<body crudo>" con tu secreto whsec_... (el secreto completo, con el prefijo).
  4. Comparalo con cada v1 en tiempo constante.
  5. Rechazá el envío si t está 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 2xx dentro de los 10 segundos. Un 3xx cuenta 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 propio t; el Facturear-Delivery y 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 id de evento), la URL, los headers y el secreto actuales del webhook, y un Facturear-Delivery nuevo.
  • Sin duplicados de nuestro lado: cada evento se arma una sola vez (por ejemplo, un invoice.completed por factura). Igual pueden llegarte repetidos por los reintentos o por un reintento manual, así que deduplicá por el id del body.
Respondé 200 apenas verificaste la firma y procesá el evento aparte (una cola, un job). Si tu proceso tarda más de 10 segundos, la entrega se da por fallida y se reintenta aunque la hayas procesado.

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. GET y PUT no 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.

Recursos relacionados