n8n: HTTP Request para llamar y Code para verificar
Tampoco hay nodo oficial, y aquí importa menos que en ningún sitio: n8n tiene un nodo de código, y eso es lo que hace falta para comprobar la firma de un webhook antes de fiarse de él.
La API está en marcha. Esto es lo que hay hoy y lo que no
Funciona. Las rutas de esta documentación están desplegadas y respondiendo en https://erp.cairos.es/api/v1/. Los ejemplos de estas páginas se pueden copiar y ejecutar. La especificación completa está en openapi.json, que es lo que importan Make y n8n.
Las claves te las creas tú, desde Desarrolladores en tu cuenta de Cairos. Empieza con una cai_test_: trabaja contra tus datos de verdad pero no registra en VeriFactu ni envía correos, así que puedes montar tu integración sin ensuciar una serie de facturación.
Y lo que todavía NO hay, dicho sin adornos: ningún conector oficial. Ni app de Shopify, ni plugin de WooCommerce, ni módulo publicado en Make o n8n. Con la API y su especificación se pueden construir —para eso están las guías de esta sección— pero construirlos es trabajo, y ese trabajo no está hecho.
Si montas algo con esto, escríbenos a hola@cairos.es. Nos interesa especialmente lo que te falte del contrato: es lo que decide qué se amplía primero.
No hay nodo oficial de Cairos en n8n
No está publicado y no hay fecha. Lo que hay es la API y esta página, que te dice exactamente qué enganchar y con qué código. Es menos cómodo que instalar algo y es lo que hay hoy; decirlo así te ahorra buscar un conector que no existe.
La credencial, que se monta una vez
n8n no tiene nodo de Cairos, y no hace falta: el nodo HTTP Request llama a cualquier API. Lo que sí conviene montar bien desde el principio es la credencial, para no repetir la clave en cada nodo ni dejarla escrita en el flujo.
Se crea una credencial de tipo Header Auth con estos dos valores:
Name: Authorization
Value: Bearer cai_live_TU_CLAVEA partir de ahí, cada nodo HTTP Request usa esa credencial y no vuelve a saber nada de la clave. Cuando la rotes, se cambia en un sitio.
Llamar a Cairos
Un nodo HTTP Request por operación. Lo que hay que rellenar es siempre lo mismo:
| Campo del nodo | Valor |
|---|---|
| Method | POST |
| URL | https://erp.cairos.es |
| Authentication | Genérica, con la credencial Header Auth de arriba |
| Send Body | Activado, en JSON |
| Headers | Idempotency-Key, con el identificador del pedido del nodo anterior |
Y activa el reintento del propio nodo sólo si has puesto la llave de idempotencia. Un reintento de un POST sin llave es la forma más rápida de acabar con dos facturas.
El cuerpo, en modo JSON, con las expresiones de n8n para tomar los datos del nodo anterior:
{
"contacto_id": "{{ $json.id }}",
"serie": "WEB",
"fecha": "{{ $now.format('yyyy-MM-dd') }}",
"lineas": [
{
"concepto": "{{ $('Pedido').item.json.titulo }}",
"cantidad": 1,
"precio": 180.00,
"iva": 21
}
]
}Recibir sucesos de Cairos, que es donde n8n brilla
Aquí es donde n8n gana a las alternativas sin código: tiene un nodo de código donde se puede comprobar la firma de un webhook, que es lo único que no es negociable al abrir un extremo público.
El montaje son tres nodos:
| # | Nodo | Qué hace |
|---|---|---|
| 1 | Webhook | Recibe el POST de Cairos. Hay que activar la opción de cuerpo sin procesar: sin ella no se puede comprobar la firma. |
| 2 | Code | Comprueba la firma con el secreto de la suscripción y corta si no cuadra. |
| 3 | Lo que quieras | Avisar por Slack, escribir en una hoja, actualizar el pedido en tu tienda. |
const crypto = require("crypto");
const secreto = $env.CAIROS_SECRETO;
const entrada = $input.first().json;
// El cuerpo CRUDO, tal y como llegó. Si aquí usas el JSON ya
// convertido, la firma no cuadrará nunca y perderás una tarde.
const crudo = entrada.rawBody || entrada.body;
const cabecera = entrada.headers["cairos-firma"] || "";
const partes = Object.fromEntries(
cabecera.split(",").map((t) => t.trim().split("="))
);
const ahora = Math.floor(Date.now() / 1000);
if (!partes.t || Math.abs(ahora - Number(partes.t)) > 300) {
throw new Error("Suceso caducado o sin marca de tiempo");
}
const esperada = crypto
.createHmac("sha256", secreto)
.update(partes.t + "." + crudo)
.digest("hex");
if (esperada !== partes.v1) {
throw new Error("Firma no valida");
}
return [{ json: JSON.parse(crudo) }];Cómo se calcula esa firma y por qué la marca de tiempo va dentro está en webhooks. Y la dirección que te da el nodo Webhook es la que se registra con POST /webhooks.
Recorrer listas largas
Para leer todas las facturas de un año no vale una sola llamada: la API pagina con cursor. En n8n eso se monta con el paginado del propio nodo HTTP Request, o a mano con un bucle:
- Pide con
?limite=200. - Guarda
siguientede la respuesta. - Vuelve a pedir con
?desde=y ese valor. - Para cuando
siguientevenga anull, no cuandodatosvenga vacío.
El detalle de la paginación, con el bucle escrito en JavaScript y en PHP, está en errores y paginación.
Preguntas sobre n8n
Authorization y valor Bearer más la clave. Los nodos la referencian y no la contienen, así que al rotarla se cambia en un sitio.Idempotency-Key. Un reintento de un POST sin llave es exactamente cómo se acaba con dos facturas del mismo pedido.Deja el flujo montado y espera la clave
Las rutas, las cabeceras y la firma son las definitivas. Cuando la API abra, sólo hay que rellenar la credencial.
HTTP Request · Header Auth · Cuerpo sin procesar en el Webhook