Autenticación y permisos
Cómo funciona la credencial, qué roles y permisos existen, cómo crear claves con permisos mínimos, rotarlas y cómo se aíslan las empresas.
La credencial
Cada solicitud /v1 lleva la clave en el encabezado X-API-Key:
GET /v1/company HTTP/1.1
Host: 127.0.0.1:3000
X-API-Key: tf_…- La clave fija empresa, ambiente y actor. No hay encabezado ni campo para cambiar de empresa o ambiente.
- El secreto se entrega una sola vez, al crear la clave. Repetir la creación devuelve
409 SECRET_ALREADY_DELIVEREDsin volver a mostrarlo. - Un secreto perdido no se recupera: se sustituye (ver Recuperación).
- Guárdalo en un almacén de secretos. No lo pongas en URLs, argumentos de línea de comandos, logs ni en Git.
- Fuera de tu equipo, usa siempre HTTPS.
La API interna de operadores no acepta claves cliente: exige certificado TLS de cliente y token de operador (ver Operadores).
Roles y permisos
Los permisos efectivos de una solicitud son la intersección entre los permisos de la clave y los roles de su actor.
| Rol | Permisos |
|---|---|
owner | Todos. Solo existe un propietario y no se asigna por API. |
administrator | Todos, excepto company:ownership, signing:authorize y billing:purchase. |
issuer | company:read, numbering:read, documents:read, documents:write, documents:deliver |
integration | Los de issuer más webhooks:read |
finance | company:read, documents:read, billing:read, billing:purchase, exports:write |
auditor | documents:read, audit:read, exports:write |
Fuente: backend/modules/identity/identity.ts. Cada operación de la referencia indica sus permisos efectivos.
Permisos mínimos por tarea
| Tarea | Permisos |
|---|---|
| Emitir y consultar facturas y notas | documents:write, documents:read |
| Leer rangos antes de emitir | numbering:read |
| Entregar al comprador | documents:deliver (también al admitir si la entrega automática está activa) |
| Consultar empresa, readiness y capacidades | company:read |
| Consultar eventos webhook | webhooks:read |
| Crear, activar o rotar destinos webhook y reproducir eventos | webhooks:write |
| Gestionar claves | keys:write |
| Gestionar actores | actors:write |
El rol integration cubre un servicio que emite y lee eventos. Para configurar webhooks usa una clave aparte con webhooks:write, en manos de quien administra la integración.
Actores
Un actor es una persona o sistema de tu empresa. Crear un actor no crea claves.
POST /v1/company/actors(permisoactors:write,Idempotency-Key) registra un actor con rolesadministrator,issuer,finance,auditorointegration; nuncaowner.- No puedes otorgar más de lo que tienes: un administrador no crea actores de finanzas porque carece de
billing:purchase. - El correo es único entre actores activos y se guarda en minúsculas. Máximo 50 actores activos por empresa y ambiente.
GET /v1/company/actors/{id}devuelve el actor y suETag.PATCH /v1/company/actors/{id}reemplaza roles y estado conreason,If-Matche idempotencia.- El propietario no se modifica (
409 OWNER_IMMUTABLE) y un actor no se gestiona a sí mismo (409 ACTOR_SELF_MANAGEMENT). - Reducir roles o poner
status: disabledaplica desde la siguiente solicitud de todas las claves del actor. Los documentos ya admitidos siguen su curso.
Claves
POST /v1/api-keys(permisokeys:write) crea una clave para unactor_idcon una lista de permisos y, opcionalmente,expires_at. Los permisos no pueden superar la intersección de tu clave, tu actor y el actor destino.GET /v1/api-keyslista metadatos (active,revoked,expired) con paginación. Nunca devuelve secretos.POST /v1/api-keys/{id}/revocationrevoca conIf-Match.
Rotar una clave
GET /v1/api-keys/{id} para obtener su ETag y revócala.Aislamiento entre empresas
Cada clave solo ve los recursos de su empresa y ambiente. Un recurso de otra empresa responde 404, no 403, para no revelar que existe. Esto aplica a documentos, archivos, entregas, destinos y eventos webhook, actores y claves. Los listados de otra empresa aparecen vacíos.
Si operas varias empresas (por ejemplo, como proveedor de software), usa una clave distinta por empresa y ambiente, y guarda los IDs con la empresa a la que pertenecen. Los cursores de paginación tampoco se comparten entre empresas.
El aislamiento se comprueba en las pruebas de integración (npm run test:integration) y se verificó desde AWS con una segunda empresa, que recibió 404 en el evento, los intentos, el documento, los archivos, su contenido y el destino (resultado AG-001).