Una cabecera, dos clases de clave y diez permisos
Todo lo que hay que entender de la seguridad de esta API cabe en tres ideas: la clave va en Authorization, la de pruebas trabaja sobre tus datos de verdad, y un permiso de escritura no te deja leer.
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.
La cabecera, y nada más
Toda llamada lleva la clave en Authorization, con la palabra Bearer delante y un espacio en medio. No hay cookies, no hay OAuth, no hay sesión que caduque y no hay que firmar la petición.
Authorization: Bearer cai_live_TU_CLAVE
Content-Type: application/json
Idempotency-Key: pedido-1042Sólo HTTPS. Una llamada por HTTP no se redirige a HTTPS: se rechaza. Redirigirla parece más amable y significaría que la clave ha viajado ya una vez en claro, que es exactamente lo que no puede pasar.
cai_test_ y cai_live_: la diferencia no es la que esperas
Hay dos clases de clave y se distinguen por el prefijo. Lo que las separa no es que trabajen contra bases de datos distintas:
cai_test_ | cai_live_ | |
|---|---|---|
| Sobre qué datos trabaja | Los tuyos, los mismos | Los tuyos |
| Crea facturas de verdad | Sí, con su número de serie | Sí |
| Registra en VeriFactu | No | Sí |
| Envía correos | No | Sí |
| Para qué sirve | Desarrollar sin ensuciar una serie de facturación de verdad | Producción |
Dicho de otro modo: la clave de pruebas no es un patio de recreo aislado. Es la clave normal con las dos consecuencias irreversibles apagadas.
Por qué se ha hecho así. Un entorno de pruebas separado obliga a mantener dos copias de tu empresa y a que nunca se parezcan del todo: en el de pruebas no están tus clientes reales, ni tus series, ni tus tipos de IVA, y la integración que funcionaba allí falla el primer día en producción. Aquí desarrollas contra tus datos de verdad, que es donde vas a acabar, y lo único que no ocurre es lo que no se puede deshacer: el registro en la Agencia Tributaria y el correo al cliente.
Y la contrapartida, dicha claramente: lo que crees con una clave de pruebas aparece en tu cuenta y consume numeración de serie si emites. Si vas a lanzar un bucle de doscientas facturas, usa una serie aparte para pruebas.
La clave se enseña una vez y no la guardamos
Al crearla se enseña entera, una vez. De ahí en adelante sólo se ve su huella y los últimos caracteres, lo justo para reconocer cuál es cuál en una lista.
No es una molestia de diseño: no tenemos la clave. Se guarda su huella, con la que se puede comprobar que la que llega es la buena, pero de la que no se puede reconstruir la original. Si un día alguien se lleva nuestra base de datos, no se lleva tus claves.
Las consecuencias son dos, y las dos son tuyas:
- Si la pierdes, no te la podemos recuperar. Se revoca y se crea otra.
- Si se filtra, revócala tú. Revocar es inmediato y no rompe nada de lo ya creado: las facturas que hizo esa clave siguen ahí.
Dónde no va una clave
No va en el código fuente, ni en el repositorio, ni en el JavaScript de una página web, ni en una aplicación móvil, ni en una captura de pantalla de una incidencia. Todo eso es publicarla. Va en una variable de entorno del servidor o en el gestor de secretos que uses.
Los diez scopes, y la trampa que tienen
Cada clave lleva la lista de lo que puede hacer. Se eligen al crearla y no se amplían solas.
Un scope de escritura no incluye el de lectura
Es la única regla contraintuitiva de todo esto y provoca el 403 más repetido. Una clave con invoices:write puede crear una factura y no puede leerla. Si tu integración crea y luego consulta —que es lo normal— necesita los dos: invoices:read y invoices:write.
| Scope | Qué abre | Recurso |
|---|---|---|
contacts:read | Leer contactos: clientes, proveedores y sus datos fiscales. | /contactos |
contacts:write | Crear y modificar contactos. | /contactos |
products:read | Leer el catálogo de productos y servicios. | /productos |
products:write | Crear y modificar productos. | /productos |
invoices:read | Leer facturas de venta, sus líneas y su estado. | /facturas |
invoices:write | Crear facturas, modificar borradores y emitir. | /facturas |
expenses:read | Leer gastos y facturas de proveedor. | /gastos |
expenses:write | Crear y modificar gastos. | /gastos |
payments:read | Leer cobros y su asignación a facturas. | /cobros |
payments:write | Registrar cobros. | /cobros |
webhooks:manage | Crear, listar y borrar suscripciones a sucesos. | /webhooks |
GET /organizacion no pide ninguno: si la clave es válida, responde. Es la llamada con la que conviene comprobar que la autenticación funciona antes de pelearse con los permisos.
Cómo elegirlos. Da el mínimo que hace falta y da el mínimo de verdad, no «los de facturas por si acaso». Tres casos que cubren casi todo:
| Lo que estás montando | Lo que necesita |
|---|---|
| Una tienda que factura sola | contacts:read contacts:write invoices:read invoices:write payments:write |
| Un panel que sólo enseña datos | invoices:read expenses:read payments:read |
| Una asesoría que descarga los libros de un cliente | invoices:read expenses:read contacts:read |
Fíjate en el tercero: sin un solo permiso de escritura, esa clave no puede estropear nada aunque el código que la usa tenga un fallo. Ése es todo el sentido de que existan los scopes.
401 y 403 no son lo mismo, y decirlo ahorra una tarde
Se confunden todo el rato y significan cosas muy distintas:
| Código | Qué dice | Qué hacer |
|---|---|---|
401 no_autenticado | No sé quién eres. No ha llegado clave, o la que ha llegado no vale, o está revocada. | Revisa la cabecera. Reintentar no sirve de nada: va a fallar igual. |
403 sin_permiso | Sé quién eres y no puedes. La clave es buena; le falta el scope de esa operación. | Mira error.detalles, que dice qué scope faltaba, y crea una clave con él. |
La regla para acordarse: el 401 se arregla con una clave distinta; el 403, con una clave mejor.
Rotar, revocar y sobrevivir a las dos cosas
Una clave no caduca sola. Aun así conviene cambiarla de vez en cuando, y hay un día en que no queda más remedio: cuando se ha filtrado.
La forma de hacerlo sin cortar el servicio es la de siempre y no tiene misterio: crear la nueva, ponerla en producción, comprobar que funciona y sólo entonces revocar la vieja. Como las claves no se pueden leer después de creadas, el orden importa: si revocas primero y la nueva no estaba bien puesta, te quedas sin las dos.
Y una recomendación que se agradece a los seis meses: una clave por integración, no una para todo. Cuando algo falle vas a querer saber qué sistema lo hizo, y cuando algo se filtre vas a querer revocar sólo eso.
Lo que hacemos nosotros con esto
Guardar bien tus claves es la mitad del trabajo; la otra mitad es nuestra. Las claves se guardan como huella y no en claro, cada organización está aislada de las demás y los servidores y las copias están en la Unión Europea. Lo que todavía no tenemos —doble factor, ver y cerrar sesiones abiertas, certificaciones— está escrito sin adornos en seguridad y datos, incluida la lista de lo que falta.
Si encuentras un fallo de seguridad en la API, escríbenos a hola@cairos.es con el asunto «Seguridad» antes de contarlo en ningún otro sitio.
Preguntas sobre claves y permisos
invoices:write y invoices:read.401 dice «no sé quién eres» y se arregla con otra clave. El 403 dice «sé quién eres y no puedes» y se arregla con una clave que tenga el scope que falta.Pide una clave de pruebas y monta la primera llamada
Con el mínimo de scopes que necesites. Ampliarlos después es una clave nueva, y eso es rápido.
Sólo HTTPS · Clave por integración · Revocar es inmediato