Scopes e irreversibilidad
Cómo leer el scope requerido y la irreversibilidad de cada endpoint desde la especificación OpenAPI — las extensiones x-required-scope y x-irreversible — y el catálogo de operaciones irreversibles.
Cada endpoint público declara dos piezas de metadata de seguridad en la
especificación OpenAPI: el scope exacto que enforza y si la operación se puede
deshacer. Los clientes (el CLI, los agentes, tu propio tooling)
las leen para fallar pronto — bloquear una llamada cuando la key carece del
scope, confirmar antes de una acción irreversible — en lugar de descubrir el
problema por un 403 o una mutación irrecuperable.
Leerlo desde la especificación
Cada operación en la especificación OpenAPI lleva dos extensiones personalizadas:
{
"operationId": "public-api.v1.invoices.delete",
"x-required-scope": "invoices:delete",
"x-irreversible": true
}x-required-scope— el único scoperesource:actionque la API key debe tener para llamar a la operación. Presente en todas las operaciones salvoGET /v1/me, que no necesita ningún scope: cualquier credencial válida puede consultarse a sí misma.x-irreversible—truesolo en las operaciones que no se pueden deshacer. Ausente (tratado comofalse) en todo lo demás.
Son extensiones de proveedor x-*, así que un visor OpenAPI genérico puede no
renderizarlas — pero cualquier cliente que parsee la especificación (como el
CLI) las lee directamente. Genera un cliente desde la especificación y heredas
ambas.
Scopes
Los scopes son resource:action (p. ej. invoices:read, contacts:delete) — el
mismo catálogo cerrado que usan la REST API y las API keys. Una petición cuya
key carece del scope devuelve 403 con code: insufficient_scope. Pide solo
los scopes que tu integración necesita.
Una operación exige un segundo scope además de x-required-scope cuando un
campo amplía lo que lee: POST /v1/companies/{company}/purchase-invoices con
source_purchase_invoice_id necesita también purchase_invoices:read, porque
copia las líneas de otro documento. Consulta
Líneas de compra medidas.
Las operaciones de control horario llevan sus propios scopes — employees:*,
time_entries:*, work_schedules:*, absences:*, presence:read,
holidays:read y payroll_exports:read — todos tras el módulo
control_horario. Consulta Control horario.
Las automatizaciones hacen lo mismo con automations:read, automations:write,
automations:delete y automation_runs:read, tras el módulo automations.
Consulta Automatizaciones.
El modelo de cuentas añade sus propios scopes. account:read, account:write y
account:owner cubren la propia cuenta (sus keys, su consumo y su titularidad),
members:*, invitations:* y nif_transfers:* su equipo y sus traspasos de NIF
(necesitan el módulo user_management), y companies:* su cartera de NIF.
Consulta
Autenticación › Administración de la cuenta.
Dos scopes son privilegiados: accounts:read y accounts:write. Permiten que
una cuenta aprovisione, administre y cobre cuentas gestionadas para sus clientes, y
funcionan distinto de cualquier otro scope: el super-scope * no los cubre,
Factuarea concede el privilegio cuenta a cuenta y lo comprueba en cada petición
(403 privileged_scope_required si falla),
y el consentimiento OAuth nunca los concede, así que usa una API key. Consulta
Scopes privilegiados y
Cuentas gestionadas.
Los documentos de control (DeCA) usan transport_documents:read,
transport_documents:write (borradores y envío por email), transport_documents:delete
(solo borradores) y transport_documents:transition (emitir, modificar, cerrar,
reabrir, anular y sustituir), tras el módulo transport_documents, que a su vez
requiere delivery_notes. El PDF de cada versión exige pdfs:read; el justificante de emisión
(GET /v1/companies/{company}/transport-documents/{transport_document}/issuance-proof) no es un PDF del documento sino la
exportación de su registro de emisión, así que exige transport_documents:read. Consulta
Documento de control (DeCA).
Los proyectos y las tareas usan projects:read|write|delete,
tasks:read|write|delete, users:read y notifications:read|write.
projects:* y tasks:* están tras el módulo tasks al emitir la
credencial: una key con esos scopes para una empresa sin el módulo se rechaza
con scope_not_allowed_by_plan, y sin el scope cada petición recibe
insufficient_scope. users:read y notifications:* son transversales y no
pertenecen a ningún módulo. Los dos scopes de borrado son solo de API key: un
consentimiento OAuth nunca los concede. Facturar las horas de un proyecto
(POST /v1/companies/{company}/projects/{project}/time-invoices) exige invoices:write, no un scope
de proyectos, porque crea una factura. Consulta
Proyectos y tareas.
El catálogo completo — cada scope, qué concede, y los sensibles — está en
Scopes y permisos. Un scope no siempre se deriva del nombre
del recurso: algunos endpoints enforzan un scope distinto del que adivinarías
(las descargas de PDF enforzan pdfs:read, los métodos de pago enforzan
invoices:read, las transiciones de estado enforzan un scope :transition, las
acciones de VeriFactu enforzan verifactu:*). Lee siempre x-required-scope en
lugar de inferirlo.
Una API key puede tener el super-scope *, que satisface cualquier
x-required-scope salvo los privilegiados accounts:read y
accounts:write. * no cubre accounts:*: una key que solo tenga *
responde 403 insufficient_scope en esas operaciones, por amplia que parezca.
Lista accounts:* de forma explícita, en una cuenta que tenga el privilegio.
Consulta el super-scope.
Operaciones irreversibles
Una operación marcada x-irreversible: true no tiene deshacer: borra datos,
emite un registro fiscal, transiciona un documento a un estado terminal, rota o
revoca un secret, o cambia quién es titular de una cuenta, quién la alcanza o
quién la paga. El CLI pide una confirmación
tipada antes de ejecutar una; tu propio tooling debería protegerlas igual.
Estas son las categorías que llevan x-irreversible: true:
| Categoría | Ejemplos | Scope |
|---|---|---|
| Borrados (individuales) | contacts.delete, invoices.delete, products.delete, purchase_invoices.delete, webhook_endpoints.delete… | <resource>:delete |
| Borrados de subrecursos de catálogo | products.presentations.delete, products.variants.delete, products.supplier-offers.delete, price-lists.delete, price-lists.items.delete, price-lists.items.purge_retired | products:write / price_lists:write |
| Borrados masivos | invoices.bulk_delete, contacts.bulk_delete, products.bulk_delete… | <resource>:delete |
| Borrados de tareas y proyectos | tasks.delete, tasks.bulk_delete, tasks.comments.delete, tasks.attachments.delete, tasks.time_entries.delete, task_labels.delete, projects.delete, projects.columns.delete, projects.custom_fields.delete: las nueve operaciones irreversibles del módulo | tasks:delete / projects:delete |
| Emisión / numeración fiscal | invoices.send, invoices.mark_sent, invoices.assign_real_number, invoices.corrective, invoices.substitute_simplified | invoices:send / invoices:write |
| Void / anulación | invoices.void, invoices.annul | invoices:void |
| Conversiones terminales | quotes.convert, proformas.convert, delivery_notes.convert | <resource>:transition |
| Cancelar / firmar | delivery_notes.cancel, delivery_notes.sign, recurring_invoices.cancel, recurring_invoices.generate | <resource>:transition / :write |
| VeriFactu (AEAT) | invoices.verifactu_create, verifactu.records.subsanar, verifactu.settings.update, verifactu.certificates.revoke | verifactu:write |
| Secret / certificado | webhook_endpoints.rotate_secret, verifactu.certificates.revoke | webhooks:write / verifactu:write |
| Traspaso de NIF | account.nif_transfers.create, account.nif_transfers.accept, account.nif_transfers.revoke | nif_transfers:write |
| Titularidad y equipo de la cuenta | account.owner.transfer, account.members.delete | account:owner / members:write |
| Keys y webhooks de la cuenta | account.api_keys.revoke, account.api_keys.rotate_secret, account.webhooks.delete, account.webhooks.rotate_secret | account:write / webhooks:delete / webhooks:write |
| Cuentas gestionadas (privilegiado) | managed_accounts.create, managed_accounts.claim_tokens.create, managed_accounts.management.end, managed_accounts.plan.update, managed_accounts.imports.create | accounts:write |
| Olvido GDPR | delivery_notes.signature_audits.forget | delivery_notes:gdpr_forget |
| Documentos de control | transport_documents.issue, transport_documents.modify, transport_documents.cancel, transport_documents.supersede, transport_documents.send | transport_documents:transition / transport_documents:write |
| Envío FacturaE (B2G) | invoices.face_submissions.submit, face_submissions.cancel | facturae:write |
| Sellado del cierre mensual | monthly_time_record_closes.seal | time_entries:write |
| Relanzamiento (reejecución) | automations.runs.replay, automations.runs.steps.replay, integrations.events.replay | automations:write / integration_events:write |
Quitar de una tarea una relación, un enlace externo, un vínculo con una entidad o
una etiqueta no es irreversible —cada uno se puede volver a crear— y solo
necesita tasks:write.
Esta lista es el resumen legible. La fuente de verdad legible por máquina
es x-irreversible en la especificación — un cliente que la lee se mantiene
correcto aunque el catálogo crezca.
Juntándolo todo
Un cliente seguro hace dos comprobaciones antes de una mutación:
- Scope-check — ¿tiene la key el
x-required-scope? Si no, para en local (sin gastar un round trip). El CLI sale con4; consulta scope-check. Una operación sin la extensión (GET /v1/me) no necesita comprobación, y paraaccounts:*el scope es necesario pero no suficiente: la cuenta también debe tener el privilegio, queGET /v1/meinforma enaccount.is_provideryaccount.privileged_scopes. - Confirmación de irreversibilidad — ¿es
x-irreversibletrue? Si lo es, confirma antes de llamar. El CLI requiere--confirm <id>; consulta operaciones irreversibles.
Los SDKs oficiales y el CLI hacen ambas por ti. Si generas tu propio cliente desde la especificación, cablea estas dos comprobaciones tú mismo desde las extensiones.
Permisos de tiendas
La gestión de tiendas usa stores:read y stores:write; los scopes del proveedor son woocommerce_store:read, woocommerce_store:write, shopify_store:read y shopify_store:write. Las pruebas 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 tabla de operaciones de tiendas.