Saltar al contenido principal

Webhooks

Los webhooks de Frihet permiten recibir notificaciones HTTP en tiempo real cuando ocurren eventos en tu cuenta. En lugar de hacer polling a la API, configura una URL y Frihet enviara un POST con los datos del evento en el momento en que se produzca.

Configuracion

  1. Ve a Configuracion > Webhooks en tu cuenta de Frihet
  2. Pulsa Crear webhook
  3. Introduce un nombre descriptivo, la URL de destino y selecciona los eventos que quieres recibir
  4. Opcionalmente, define un secreto para la verificacion HMAC (recomendado)
  5. Guarda el webhook

Requisitos de la URL:

  • Debe usar HTTPS (se permite HTTP solo para localhost y 127.0.0.1 durante el desarrollo)
  • No se permiten IPs privadas (10.x, 172.16-31.x, 192.168.x) para evitar ataques SSRF
  • Debe responder con un codigo 2xx en menos de 30 segundos

Limites:

  • Maximo 20 webhooks por cuenta
  • Payload maximo de 100 KB por entrega

Tipos de evento

Frihet emite 14 tipos de evento, agrupados por recurso.

Facturas

EventoDescripcion
invoice.createdSe ha creado una nueva factura
invoice.updatedSe ha modificado una factura existente
invoice.paidUna factura ha sido marcada como pagada
invoice.overdueUna factura ha superado su fecha de vencimiento

Gastos

EventoDescripcion
expense.createdSe ha registrado un nuevo gasto
expense.updatedSe ha modificado un gasto existente

Presupuestos

EventoDescripcion
quote.createdSe ha creado un nuevo presupuesto
quote.updatedSe ha modificado un presupuesto existente
quote.acceptedUn cliente ha aceptado un presupuesto
quote.rejectedUn cliente ha rechazado un presupuesto

Clientes

EventoDescripcion
client.createdSe ha dado de alta un nuevo cliente
client.updatedSe han modificado los datos de un cliente

Productos

EventoDescripcion
product.createdSe ha creado un nuevo producto o servicio
product.updatedSe ha modificado un producto o servicio existente

Estructura del payload

Cada entrega de webhook es una peticion POST con cuerpo JSON. La estructura es la misma para todos los tipos de evento:

{
"event": "invoice.paid",
"timestamp": "2026-02-12T14:30:00.000Z",
"data": {
"id": "inv_abc123",
"clientName": "Acme S.L.",
"items": [
{ "description": "Consultoria", "quantity": 10, "unitPrice": 75 }
],
"total": 750,
"status": "paid",
"paidAt": "2026-02-12T14:29:58.000Z",
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-02-12T14:30:00.000Z"
}
}

El campo data contiene el estado completo del recurso en el momento del evento.


Cabeceras de entrega

Cada peticion de webhook incluye las siguientes cabeceras:

CabeceraDescripcionEjemplo
Content-TypeTipo de contenidoapplication/json
X-Frihet-EventTipo de eventoinvoice.paid
X-Frihet-Delivery-IdIdentificador unico de la entregad4e5f6a7b8c9
X-Frihet-TimestampMarca de tiempo ISO 86012026-02-12T14:30:00.000Z
X-Frihet-SignatureFirma HMAC-SHA256 del payloadsha256=a1b2c3d4e5f6...

La cabecera X-Frihet-Signature solo se incluye si has configurado un secreto en el webhook.


Verificacion de firma

Si configuras un secreto al crear el webhook, Frihet firma cada payload con HMAC-SHA256. El proceso de verificacion consiste en:

  1. Tomar el cuerpo de la peticion en bruto (raw body)
  2. Calcular el HMAC-SHA256 usando el secreto como clave
  3. Comparar el resultado con el valor de la cabecera X-Frihet-Signature (sin el prefijo sha256=)

Ejemplo en Node.js

const crypto = require('crypto');

function verifyWebhookSignature(rawBody, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');

const receivedSignature = signature.replace('sha256=', '');

return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'hex'),
Buffer.from(receivedSignature, 'hex')
);
}

// Uso en un servidor Express
app.post('/webhook/frihet', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-frihet-signature'];
const secret = process.env.FRIHET_WEBHOOK_SECRET;

if (!signature || !verifyWebhookSignature(req.body.toString(), signature, secret)) {
return res.status(401).send('Firma invalida');
}

const event = JSON.parse(req.body.toString());
const eventType = req.headers['x-frihet-event'];

console.log(`Evento recibido: ${eventType}`, event.data);

// Procesar el evento segun su tipo
switch (eventType) {
case 'invoice.paid':
// Actualizar tu sistema de contabilidad
break;
case 'expense.created':
// Notificar al equipo financiero
break;
}

res.status(200).send('OK');
});

Ejemplo en Python

import hmac
import hashlib
from flask import Flask, request, abort

app = Flask(__name__)
WEBHOOK_SECRET = 'tu-secreto-aqui'

def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()

received = signature.replace('sha256=', '')

return hmac.compare_digest(expected, received)

@app.route('/webhook/frihet', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Frihet-Signature', '')
if not verify_signature(request.data, signature, WEBHOOK_SECRET):
abort(401)

event = request.get_json()
event_type = request.headers.get('X-Frihet-Event')

print(f'Evento recibido: {event_type}')

if event_type == 'invoice.paid':
# Actualizar tu sistema de contabilidad
pass
elif event_type == 'quote.accepted':
# Convertir presupuesto a factura
pass

return 'OK', 200

Importante: Usa siempre una comparacion en tiempo constante (timingSafeEqual en Node.js, compare_digest en Python) para evitar ataques de timing.


Politica de reintentos

Si la entrega de un webhook falla (codigo de respuesta no-2xx o timeout), Frihet reintenta automaticamente con backoff exponencial:

IntentoRetardoTiempo acumulado
1 (inicial)inmediato0s
2 (primer reintento)2 segundos2s
3 (segundo reintento)4 segundos6s
  • Maximo 3 intentos por entrega (1 inicial + 2 reintentos)
  • Retardo maximo de 30 segundos entre reintentos
  • Timeout de 30 segundos por peticion
  • Si los 3 intentos fallan, la entrega se marca como failed

Los reintentos se procesan mediante un job que se ejecuta cada 5 minutos, garantizando la fiabilidad incluso si el proceso principal se reinicia.

Puedes consultar el historial de entregas de cada webhook desde el panel de Frihet, incluyendo el codigo de respuesta, el cuerpo de la respuesta (primeros 1000 caracteres) y los errores de cada intento.


Pruebas

Desde el panel de Frihet puedes enviar un evento de prueba a cualquier webhook configurado. El payload de prueba tiene la siguiente estructura:

{
"event": "webhook.test",
"timestamp": "2026-02-12T14:30:00.000Z",
"data": {
"message": "This is a test webhook from Frihet ERP"
}
}

Esto permite verificar que tu endpoint esta accesible, que la firma se valida correctamente y que tu sistema procesa los eventos sin errores.


Buenas practicas

Responde rapido

Tu endpoint debe responder con un codigo 200 lo antes posible. Si necesitas realizar un procesamiento pesado (enviar emails, actualizar bases de datos externas, etc.), acepta el evento y procesalo de forma asincrona en una cola de trabajo.

Gestiona la idempotencia

Es posible que un mismo evento se entregue mas de una vez (por ejemplo, si tu servidor respondio con un timeout pero proceso el evento). Usa el campo X-Frihet-Delivery-Id como clave de idempotencia para evitar duplicados.

Verifica la firma siempre

Nunca confies en un webhook sin verificar la cabecera X-Frihet-Signature. Cualquier actor con acceso a tu URL podria enviar payloads falsos.

Usa HTTPS

En produccion, tu endpoint debe estar protegido con HTTPS. Frihet rechaza URLs HTTP (excepto en desarrollo local).

Monitoriza los fallos

Revisa periodicamente los logs de entrega en el panel de Frihet. Si ves entregas fallidas de forma recurrente, verifica que tu endpoint esta disponible y responde en menos de 30 segundos.

Filtra los eventos

Suscribete solo a los eventos que necesitas. Cada webhook puede escuchar uno o varios tipos de evento. Cuantos menos eventos innecesarios proceses, menor sera la carga en tu servidor.


Resolucion de problemas

No recibo webhooks

  1. Verifica que la URL del webhook es correcta y accesible desde internet
  2. Comprueba que el webhook esta en estado activo en el panel
  3. Revisa los logs de entrega para ver si hay errores
  4. Si usas un firewall, asegurate de que las peticiones POST entrantes desde Google Cloud (us-central1) estan permitidas

Las firmas no coinciden

  1. Verifica que estas usando el raw body de la peticion (antes de parsear el JSON)
  2. Confirma que el secreto en tu codigo coincide con el configurado en el panel de Frihet
  3. No modifiques ni reformatees el body antes de verificar la firma
  4. Revisa que tu framework no esta parseando automaticamente el body antes de que puedas acceder al raw

Los reintentos no llegan

  1. Los reintentos se procesan cada 5 minutos. Si tu endpoint estuvo caido brevemente, puede que ya se hayan agotado los intentos
  2. Revisa el historial de entregas para confirmar el estado de cada intento
  3. Si los 3 intentos fallaron, la entrega no se reintentara de nuevo automaticamente

Payload demasiado grande

Si el recurso asociado al evento es muy grande (muchas lineas en una factura, campos extensos), el payload puede superar el limite de 100 KB y la entrega sera rechazada. Simplifica los datos del recurso o contacta con soporte si necesitas un limite superior.