API keys (autoservicio)
Emite, lista, acota, rota y revoca las API keys de tu cuenta desde la API v1 — quién puede emitirlas, los environments live/test, los scopes que puedes asignar y el techo de nivel por NIF.
Más allá del dashboard de desarrollador, Factuarea expone todo el ciclo de
vida de las API keys de tu cuenta desde la API pública v1, para que
aprovisiones y rotes credenciales de forma programática. Una API key pertenece
a la cuenta, nunca a uno de sus NIF, así que vive en el eje de cuenta: seis
endpoints bajo /v1/accounts/{account}/api-keys cubren listar, crear,
recuperar, acotar, rotar el secret y revocar.
| Operación | Endpoint | Scope |
|---|---|---|
| Listar keys | GET /v1/accounts/{account}/api-keys | account:read |
| Crear una key | POST /v1/accounts/{account}/api-keys | account:write |
| Recuperar una key | GET /v1/accounts/{account}/api-keys/{api_key} | account:read |
| Cambiar los NIF de una key | PATCH /v1/accounts/{account}/api-keys/{api_key}/nif-scope | account:write |
| Rotar el secret | POST /v1/accounts/{account}/api-keys/{api_key}/rotate-secret | account:write |
| Revocar una key | POST /v1/accounts/{account}/api-keys/{api_key}/revoke | account:write |
{account} es el id opaco de la cuenta a la que pertenece la credencial —lo
lees en account.id de GET /v1/me—. Tiene que ser tu propia cuenta:
cualquier otro valor responde el mismo 404 account_not_found.
{api_key} es el id de la key — un UUID v7 opaco, no su prefijo ni su
secret. Consulta los esquemas completos en la
Referencia de la API.
Quién puede emitir una key
Una key la emite siempre una persona, y lo que alcanza se mide contra esa persona. Las keys se emiten en dos sitios:
| Dónde | Quién puede | Qué recibe la key nueva |
|---|---|---|
| La aplicación web, en Ajustes → Desarrolladores → API Keys | Un titular o administrador de la cuenta dueña del NIF que tienes seleccionado. Un miembro, un asesor externo (siempre es miembro) y una persona que alcanza el NIF solo por un acceso cedido no pueden: la aplicación web pide un administrador de la cuenta. | Los NIF que le asignes, cada uno con el nivel que te da tu rol. |
POST /v1/accounts/{account}/api-keys | Cualquier key de tu cuenta que lleve account:write. | NIF dentro del ámbito de la key que la emite, y nunca más. |
- El nivel sale de la persona. Un
ownery unadminalcanzan todos los NIF de la cuenta conoperate. Unmemberalcanza solo los NIF en los que tiene un grant, con el nivel de ese grant (roles). Una key toma en cada NIF el nivel de la persona que la emitió, y solo baja. - Una key emitida por la API responde ante dos límites. Su ámbito no puede superar el de la key que la emite, y sus niveles no pueden superar lo que tiene hoy la persona que hay detrás de esa key.
accounts:readyaccounts:writeexigen más. Son scopes privilegiados: quien emite debe ser titular o administrador, y la cuenta debe tener un privilegio que solo concede Factuarea.
Lista tus keys
GET /v1/accounts/{account}/api-keys devuelve tus keys con paginación por
cursor. Cada key expone su prefix, scopes,
tier, environment y los timestamps de su ciclo de vida — nunca el secret.
Las keys revocadas siguen en la lista, para que veas el historial.
curl https://api.factuarea.com/v1/accounts/{account}/api-keys \
-H "Authorization: Bearer fact_live_8KqW3pXnR2VbY7TcA9eFmN5z"{
"data": [
{
"id": "0190f2b1-1c4e-7a3d-9f10-0a1b2c3d4e5f",
"object": "api_key",
"name": "Production sync",
"prefix": "fact_live_8KqW3pXn",
"scopes": ["invoices:read", "invoices:write"],
"tier": "scale",
"environment": "live",
"created_at": "2026-06-01T09:30:00Z",
"last_used_at": "2026-06-23T18:04:11Z",
"expires_at": null,
"revoked_at": null,
"is_active": true,
"is_revoked": false
}
],
"has_more": false,
"next_cursor": null
}prefix son los primeros caracteres de la key — seguro de registrar en logs,
no autentica. Úsalo para reconocer una key en tus propios paneles sin
guardar nunca el secret.
El listado, la recuperación, la rotación, la revocación y el cambio de ámbito
trabajan sobre las keys cuyo ámbito entero cabe dentro del ámbito de la key con
la que te autenticas. Una key que alcanza un NIF que la tuya no alcanza no
aparece en tu listado, y pedirla responde 404 api_key_not_found.
Crea una key
POST /v1/accounts/{account}/api-keys emite una key nueva y devuelve su secret
en claro exactamente una vez. Guárdalo en el momento en que lo recibes — no
hay ningún endpoint para volver a leerlo más tarde.
curl -X POST https://api.factuarea.com/v1/accounts/{account}/api-keys \
-H "Authorization: Bearer fact_live_8KqW3pXnR2VbY7TcA9eFmN5z" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"name": "Reporting export",
"scopes": ["invoices:read", "pdfs:read"],
"scoped_company_ids": [
"0193f1a0-5c2e-7b41-8d06-9a1e2f3c4d5e",
"01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01"
]
}'Respuesta (201, con una cabecera Location que apunta a la key nueva):
{
"data": {
"id": "0190f2c0-77aa-7b21-8c33-1d2e3f405162",
"object": "api_key",
"name": "Reporting export",
"prefix": "fact_live_1N0Fnyhh",
"scopes": ["invoices:read", "pdfs:read"],
"tier": "scale",
"environment": "live",
"created_at": "2026-06-24T08:15:00Z",
"last_used_at": null,
"expires_at": null,
"revoked_at": null,
"is_active": true,
"is_revoked": false,
"secret": "fact_live_1N0FnyhhR2VbY7TcA9eFmN5z"
}
}El campo secret aparece solo en esta respuesta 201 (y tras una
rotación). No lo devuelve nunca el listado, la recuperación ni ningún otro
endpoint. Si lo pierdes debes rotar la key. Persístelo en un gestor de
secrets de inmediato — nunca en un log ni en un repositorio.
Envía una Idempotency-Key para que un reintento tras un fallo de red no cree
una segunda key. La repetición responde el mismo 201 con secret a null,
porque nunca guardamos el secret: si no recibiste la primera respuesta, revoca
esa key y crea una nueva.
Cuerpo de la petición
| Campo | Obligatorio | Notas |
|---|---|---|
name | sí | Etiqueta legible (1–120 caracteres). |
scopes | sí | Uno o más scopes del catálogo cerrado. Mínimo uno. Consulta los scopes que puedes asignar. |
environment | no | live (por defecto) o test. Consulta más abajo. |
scoped_company_ids | no | Los NIF a los que llega la key, por id (UUID v7). Si lo omites, la key llega a los mismos NIF que la key que la crea, y nunca a más. No se admite con environment: test. Consulta los NIF a los que llega una key. |
expires_at | no | Instante ISO 8601 futuro a partir del cual la key deja de autenticar. |
ip_allowlist | no | Lista opcional de IPs / rangos CIDR permitidos (IPv4, IPv6, /N). Una petición desde otra dirección responde 401 ip_not_allowed. |
El tier se deriva del plan de tu cuenta — no se puede fijar desde el
cuerpo y no existe una compra de capacidad independiente. Si envías tier, se
ignora.
Al crear una key se aplican dos topes, los dos por tier y los dos contados sobre
keys activas. max_api_keys_exceeded (422)
dice que la empresa que guarda la key ya tiene las keys que permite su plan.
portfolio_max_api_keys_exceeded
(422) se aplica cuando tu cuenta tiene dos o más NIF reales y se cuenta sobre
todas las keys activas de la cuenta, no por NIF, así que dar de alta un NIF no
lo sube. Revocar una key libera su plaza. Límites de peticiones
describe los límites de una cartera.
El campo environment
Cada key pertenece a uno de los dos environments, fijado al crearla y visible en el objeto de la key:
environment | Prefijo | Opera sobre |
|---|---|---|
live | fact_live_ | Tus NIF reales, con efectos reales (VeriFactu → AEAT, emails, webhooks). |
test | fact_test_ | La empresa sandbox de tu cuenta, aislada y con los efectos externos desactivados. |
Pasa environment: test al crear una key para emitir una credencial de
sandbox; omítelo para una key live. El prefijo refleja el environment, así que
los distingues sin decodificar la key. Consulta Modo de prueba y
sandbox para saber qué se desactiva en test.
curl -X POST https://api.factuarea.com/v1/accounts/{account}/api-keys \
-H "Authorization: Bearer fact_live_8KqW3pXnR2VbY7TcA9eFmN5z" \
-H "Content-Type: application/json" \
-d '{"name": "Integration tests", "scopes": ["invoices:read", "invoices:write"], "environment": "test"}'Una key fact_test_ pertenece a tu cuenta real — su empresa sandbox vive dentro
de ella — y eso decide lo que puede hacer con las keys:
- Alcanza solo su sandbox. Declarar
scoped_company_idsjunto conenvironment: testfalla la validación, porque una key de pruebas no tiene ningún otro NIF al que llegar. Una key live tampoco puede llevar la sandbox: mezclarlos responde422 business_rule_violationcon el subcódigonif_scope_environment_mismatch. - Lee la cuenta como una key live. Listar y recuperar keys con una key de pruebas responde igual que con una live.
- Nunca escribe. Crear, rotar, revocar o cambiar el ámbito de una key con una
key de pruebas responde
403 live_credential_requiredy no cambia nada. Eso incluye pedir una key live, y también pedir otra key de pruebas: emite las keys de pruebas con una key live o en la aplicación web.
Probar cuentas gestionadas explica en qué cuentas puede escribir una key de pruebas.
Los NIF a los que llega una key
El ámbito de una key es la lista de NIF a los que llega, y GET /v1/me lo
enumera en scope. Se fija al crear la key, y puedes cambiarlo después.
- Sin
scoped_company_idsla key llega a los mismos NIF que la key que la crea, nunca a más. Se dejan fuera los NIF en los que la persona que emite no alcanza el nivel que piden tus scopes. - Con
scoped_company_idsla key llega exactamente a esos NIF, y tienen que caber en el ámbito de la key emisora: nombrar uno fuera responde422 business_rule_violationcon el subcódigonif_scope_exceeds_issuery enumera los NIF que no caben, sin crear nada. El ámbito nunca se recorta en silencio. - Qué NIF se pueden nombrar. Los de tu cuenta, y los que tu cuenta alcanza
por una relación vigente: un acceso cedido o una
cuenta gestionada. Cualquier otro, y cualquier
identificador que no exista, responde el mismo
422 invalid_param_valueconparamscoped_company_ids, para que la respuesta no sirva para censar NIF.
PATCH /v1/accounts/{account}/api-keys/{api_key}/nif-scope
sustituye el ámbito de una key que ya desplegaste. scoped_company_ids es la
lista resultante completa, no un delta, con al menos un NIF:
curl -X PATCH \
https://api.factuarea.com/v1/accounts/{account}/api-keys/0190f2b1-1c4e-7a3d-9f10-0a1b2c3d4e5f/nif-scope \
-H "Authorization: Bearer fact_live_8KqW3pXnR2VbY7TcA9eFmN5z" \
-H "Content-Type: application/json" \
-d '{"scoped_company_ids": ["0193f1a0-5c2e-7b41-8d06-9a1e2f3c4d5e", "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01"]}'{
"data": {
"id": "0190f2b1-1c4e-7a3d-9f10-0a1b2c3d4e5f",
"object": "api_key_nif_scope",
"scope": [
{ "id": "0193f1a0-5c2e-7b41-8d06-9a1e2f3c4d5e", "tax_id": "B12345674", "name": "Distribuciones Ejemplo SL" },
{ "id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01", "tax_id": "B87654323", "name": "Talleres García SL" }
]
}
}- Solo dentro de tu ámbito. Solo puedes cambiar una key cuyo ámbito actual cabe dentro del tuyo, y el ámbito nuevo nunca puede superar el tuyo.
- Los scopes de la key se comprueban de nuevo. Cada scope que lleva la key
tiene que estar permitido por el plan de todos los NIF de la lista nueva.
Si no, la respuesta es
422 scope_not_allowed_by_plany no cambia nada. - Niveles. Un NIF que se queda conserva el nivel guardado en la key, así que cambiar el ámbito nunca lo sube. Un NIF que entra toma el nivel de la persona que emitió la key.
- Mismas respuestas que al crear. Se aplican los mismos errores
422, y una key revocada responde422 api_key_already_revoked. Una key de pruebas tiene un ámbito fijo, su sandbox: cualquier otra lista respondenif_scope_environment_mismatch.
Claves acotadas explica cómo usar una key que llega solo a una parte de la cuenta.
El techo de nivel por NIF
Cada NIF de una key tiene un nivel, view u operate. Se fija cuando el NIF
entra en la key, al emitirla o al cambiar su ámbito, y solo baja. Es el menor de
tres: el nivel de la relación, cuando el NIF es de otra cuenta a la que llegas por
un acceso cedido o una cuenta gestionada; el grant de la persona que emite la
key; y el rol de esa persona.
- Escribir exige
operate. Una key con cualquier scope que no sea de lectura (:read), incluido*, necesitaoperateen cada NIF que nombra. A una key que solo lleva scopes:readle basta conview. - Un NIF que la persona que emite solo lee. Nombrarlo junto con un scope de
escritura responde
422 business_rule_violationcon el subcódigonif_access_level_exceeds_emittery no crea nada. Si no declarasscoped_company_ids, se dejan fuera los NIF por debajo del nivel necesario, y cuando no queda ninguno la respuesta es el mismo422. - Se mide de nuevo en cada petición. Cuenta el menor entre el nivel guardado
en la key y el nivel que la persona que emite tiene en ese momento. Con
view, una escritura responde403 company_read_only, y sin ningún nivel, el mismo404 profile_not_foundque un NIF fuera del ámbito. - Se recalcula a la baja. Bajar o retirar un grant baja las keys de esa persona, y reducir o terminar un acceso entre cuentas baja las keys de la cuenta receptora en la siguiente petición. Una key que se queda sin ningún NIF se revoca. Nada sube por sí solo: para obtener un nivel más alto, emite una key nueva.
{
"error": {
"type": "invalid_request_error",
"code": "business_rule_violation",
"subcode": "nif_access_level_exceeds_emitter",
"message": "No puedes dar a la credencial más acceso del que tienes. Sin acceso suficiente en: 01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01. Una credencial con permisos de escritura solo alcanza NIF en los que operas.",
"param": "scoped_company_ids",
"doc_url": "https://docs.factuarea.com/guides/errors#business_rule_violation",
"request_id": "req_01J8Y3M2Q40000000000000011"
}
}Acceso cedido muestra el techo de una key en un NIF que otra cuenta te deja alcanzar, y qué pasa con las keys cuando ese acceso se reduce o termina.
Los scopes que puedes asignar
scopes toma valores del catálogo cerrado de
Autenticación. Un valor fuera de él falla la
validación con 422 invalid_param_value.
- El plan decide, NIF a NIF. Un scope se concede solo si el plan de todos
los NIF del ámbito lo permite, y el exceso nunca se recorta en silencio:
responde
422 scope_not_allowed_by_plan. Un módulo bloqueado en modo de prueba responde422 scope_not_allowed_in_sandbox. Los scopesmembers:*,invitations:*ynif_transfers:*necesitan el módulouser_management. accounts:readyaccounts:writeno te los puedes asignar tú. Son scopes privilegiados: una key los lleva solo mientras la cuenta tenga un privilegio que solo concede Factuarea, y solo si la persona que emite la key es titular o administrador. Si no, la creación responde403 privileged_scope_requiredy no se crea ninguna key.*no los cubre. El super-scope cubre todos los demás scopes. Con el privilegio puedes listar*yaccounts:*juntos en la misma key.
Cuentas gestionadas explica qué abre el privilegio.
Rota el secret
POST /v1/accounts/{account}/api-keys/{api_key}/rotate-secret genera un
prefix + secret nuevos y devuelve el secret nuevo en claro exactamente
una vez. El secret anterior sigue funcionando durante una ventana de
gracia de 24 horas para que puedas desplegar el nuevo sin tiempo de
inactividad. El environment de la key no cambia.
Es irreversible, así que la Idempotency-Key es obligatoria: sin ella la
respuesta es 422 idempotency_key_required.
curl -X POST \
https://api.factuarea.com/v1/accounts/{account}/api-keys/0190f2b1-1c4e-7a3d-9f10-0a1b2c3d4e5f/rotate-secret \
-H "Authorization: Bearer fact_live_8KqW3pXnR2VbY7TcA9eFmN5z" \
-H "Idempotency-Key: $(uuidgen)"{
"data": {
"id": "0190f2b1-1c4e-7a3d-9f10-0a1b2c3d4e5f",
"object": "api_key",
"name": "Production sync",
"prefix": "fact_live_Zq7mP4xV",
"scopes": ["invoices:read", "invoices:write"],
"tier": "scale",
"environment": "live",
"created_at": "2026-06-01T09:30:00Z",
"last_used_at": "2026-06-23T18:04:11Z",
"expires_at": null,
"revoked_at": null,
"is_active": true,
"is_revoked": false,
"secret": "fact_live_Zq7mP4xVnR2VbY7TcA9eFmN5z"
}
}Durante la ventana de gracia de 24 horas autentican tanto el secret nuevo
como el anterior; una petición que siga usando el anterior recibe una
cabecera 199 Warning con la cuenta atrás de horas restantes. Al expirar
la ventana el secret anterior se rechaza y se purga. Despliega el nuevo
secret dentro de esas 24 horas. La rotación es irreversible.
Revoca una key
POST /v1/accounts/{account}/api-keys/{api_key}/revoke invalida una key de forma
permanente. Las peticiones posteriores autenticadas con ella fallan con 401.
Un reason opcional (máx. 500 caracteres) queda en el audit log. Igual que la
rotación, es irreversible y exige una Idempotency-Key.
curl -X POST \
https://api.factuarea.com/v1/accounts/{account}/api-keys/0190f2b1-1c4e-7a3d-9f10-0a1b2c3d4e5f/revoke \
-H "Authorization: Bearer fact_live_8KqW3pXnR2VbY7TcA9eFmN5z" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"reason": "Rotated out of the deploy pipeline"}'La revocación es irreversible y no se limita a otras keys: puedes revocar la propia key con la que autenticas la petición, cortando tu propio acceso. Asegúrate de tener antes otra key válida si aún necesitas acceso a la API.
Tras la revocación, las peticiones con esa key devuelven 401 con el código
api_key_revoked:
{
"error": {
"type": "authentication_error",
"code": "api_key_revoked",
"message": "Esta API key ha sido revocada.",
"doc_url": "https://docs.factuarea.com/guides/errors#api_key_revoked",
"request_id": "req_01JBVH7K9Y4N3CDQ2EHJB1AGSV"
}
}El código es específico porque el secret todavía coincide: quien lo envía ya
tiene la key. Un secret que no coincide con nada responde el genérico
invalid_api_key, y una key caducada responde
api_key_expired.
Scopes y aislamiento
Los seis endpoints están protegidos por los scopes de account:
account:read— listar y recuperar keys.account:write— crear, cambiar el ámbito de, rotar y revocar keys.
Todas las operaciones se acotan a tu propia cuenta y, dentro de ella, al ámbito
de la key con la que te autenticas. Las respuestas nunca revelan lo que existe, y
ninguna es un 403:
- Un
{account}que no es tuyo, no existe o no es un identificador de cuenta responde404 account_not_found. - El
idde una key que pertenece a otra cuenta, no existe o llega a un NIF fuera de tu ámbito responde404 api_key_not_found.
Gestionar keys sigue requiriendo una key existente con los scopes adecuados. Crea tu primera key en el dashboard de desarrollador (app.factuarea.com/settings/developers/api-keys), como titular o administrador de la cuenta, y después usa estos endpoints para aprovisionar el resto de forma programática. Consulta Autenticación para el formato de la key y las cabeceras.
Errores
| Código | HTTP | Cuándo |
|---|---|---|
insufficient_scope | 403 | La key no lleva ni el scope de la operación ni *. |
live_credential_required | 403 | Una key fact_test_ intenta crear, rotar, revocar o cambiar el ámbito de una key. |
privileged_scope_required | 403 | Pides accounts:read o accounts:write y la cuenta no tiene el privilegio, o quien emite ya no es titular ni administrador. |
account_not_found | 404 | {account} no es tu cuenta. |
api_key_not_found | 404 | La key no existe, pertenece a otra cuenta o llega a un NIF fuera de tu ámbito. |
invalid_param_value | 422 | Un scope está fuera del catálogo, o scoped_company_ids nombra un NIF que no existe o que tu cuenta no alcanza. |
business_rule_violation | 422 | Subcódigos nif_scope_exceeds_issuer, nif_access_level_exceeds_emitter, nif_scope_environment_mismatch y issuer_company_outside_account. |
scope_not_allowed_by_plan | 422 | Un scope pedido no lo permite el plan de algún NIF del ámbito. |
scope_not_allowed_in_sandbox | 422 | Una key de pruebas pide un scope de un módulo bloqueado en modo de prueba. |
max_api_keys_exceeded | 422 | La empresa que guarda la key ha alcanzado las keys que permite su plan. |
portfolio_max_api_keys_exceeded | 422 | La cuenta ha alcanzado las keys activas que permite una cartera. |
api_key_already_revoked | 422 | Intentas rotar, revocar o cambiar el ámbito de una key que ya está revocada. |
idempotency_key_required | 422 | Rotas o revocas sin Idempotency-Key. |
Tools MCP
Las mismas operaciones son tools MCP: list_api_keys, get_api_key,
create_api_key, rotate_api_key_secret, revoke_api_key y
update_api_key_nif_scope. Las dos lecturas también se pueden usar por OAuth.
Las cuatro escrituras necesitan una API key, porque el consentimiento OAuth nunca
concede account:write. Consulta Scopes de MCP.
Permisos de tiendas
La gestión de tiendas usa stores:read y stores:write; los scopes propios de cada proveedor son woocommerce_store:read, woocommerce_store:write, shopify_store:read y shopify_store:write. Las operaciones de prueba de conexión requieren el scope de escritura del proveedor. Estos scopes se asignan a API keys y quedan fuera del consentimiento OAuth. Consulta la matriz de operaciones en tiendas ecommerce.
Relacionado
keys que llegan solo a una parte de la cuenta.
el formato de la key, el catálogo de scopes, los scopes privilegiados y OAuth.
los roles del equipo, los grants por NIF y GET /v1/me.
el nivel de una key en un NIF de otra cuenta y qué pasa cuando el acceso se reduce.
qué desactiva una key fact_test_.