ffacturifirma

Documentación

Webhooks

Recibe notificaciones HTTP firmadas en tus sistemas cada vez que un documento cambia en facturifirma.ro.

Registrar un webhook

Desde la app: página Webhooks (menú de administración) — añade la URL de destino y un secreto de firma. El secreto es write-only: no puede leerse después de guardar.

Vía API, con una clave API de administrador:

curl -X POST https://app.facturifirma.ro/api/v1/webhooks \
  -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/webhook", "secret": "<SIGNING_SECRET>"}'

Eventos

Actualmente se envía un único tipo de evento:

document.changedcualquier cambio de un documento — creación, edición, emisión, cobro, rectificación, anulación o borrado, para todos los tipos de documento (factura, proforma, recibo, albarán, pedido, factura rectificativa).

Se añadirán nuevos tipos de evento con el tiempo; trata el campo "event" como extensible e ignora los tipos desconocidos.

Payload

Cada entrega es un POST con cuerpo JSON:

POST https://example.com/webhook

{
  "event": "document.changed",
  "documentId": "8b1f7c02-4c33-4a6e-9f0d-2f6f6f0a9d11",
  "at": "2026-07-15T09:30:00Z"
}

Cabeceras

Cada petición lleva el tipo de evento y la firma del cuerpo:

Content-Type: application/json
X-Facturifirma-Event: document.changed
X-Facturifirma-Signature: sha256=<hex(HMAC-SHA256(secret, body))>

Verificar la firma

La firma es HMAC-SHA256 sobre el cuerpo bruto de la petición con tu secreto, en formato "sha256=<hex>". Compárala en tiempo constante:

import crypto from "node:crypto";

function verify(rawBody, signatureHeader, secret) {
  const expected = "sha256=" +
    crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signatureHeader), Buffer.from(expected));
}

Entrega y reintentos

Las entregas salen de una cola de trabajos con reintentos automáticos (at-least-once, sin garantía de orden). Tu endpoint debe responder 2xx; cualquier otra cosa cuenta como fallo y se reintenta.

Seguridad

Solo se aceptan URLs http(s) absolutas que resuelvan a direcciones públicas (protección SSRF). El secreto de firma se cifra en reposo y la API nunca lo devuelve.