Scopes & irreversibility
How to read the required scope and irreversibility of each endpoint from the OpenAPI spec — the x-required-scope and x-irreversible extensions — and the catalog of irreversible operations.
Every public endpoint declares two pieces of safety metadata in the OpenAPI
spec: the exact scope it enforces and whether the operation can be undone.
Clients (the CLI, agents, your own tooling) read them to fail
fast — block a call when the key lacks the scope, confirm before an irreversible
action — instead of discovering the problem from a 403 or an unrecoverable
mutation.
Reading it from the spec
Each operation in the OpenAPI spec carries two custom extensions:
{
"operationId": "public-api.v1.invoices.delete",
"x-required-scope": "invoices:delete",
"x-irreversible": true
}x-required-scope— the singleresource:actionscope the API key must hold to call the operation. Present on every operation.x-irreversible—trueonly on operations that cannot be undone. Absent (treated asfalse) on everything else.
These are x-* vendor extensions, so a generic OpenAPI viewer may not render
them — but any client that parses the spec (like the CLI) reads them directly.
Generate a client from the spec and you inherit both.
Scopes
Scopes are resource:action (e.g. invoices:read, contacts:delete) — the
same closed catalog the REST API and the API keys use. A request whose key
lacks the scope returns 403 with code: insufficient_scope. Request only the
scopes your integration needs.
One operation enforces a second scope on top of x-required-scope when a
field widens what it reads: POST /v1/purchase_invoices with
source_purchase_invoice_id also needs purchase_invoices:read, because it
copies the lines of another document. See
Measured purchase lines.
Time-tracking operations carry their own scopes — employees:*,
time_entries:*, work_schedules:*, absences:*, presence:read,
holidays:read and payroll_exports:read — all gated behind the
control_horario module. See Time tracking.
Automations do the same with automations:read, automations:write,
automations:delete and automation_runs:read, gated behind the automations
module. See Automations.
The full catalog — every scope, what it grants, and the sensitive ones — is in
Scopes & permissions. A scope is not always derivable
from the resource name: some endpoints enforce a different scope than you'd
guess (PDF downloads enforce pdfs:read, payment methods enforce
invoices:read, state transitions enforce a :transition scope, VeriFactu
actions enforce verifactu:*). Always read x-required-scope rather than
inferring it.
An API key may hold the super-scope *, which satisfies any
x-required-scope. See Authentication.
Irreversible operations
An operation marked x-irreversible: true has no undo: it deletes data,
emits a fiscal record, transitions a document to a terminal state, or rotates a
secret. The CLI asks for a typed
confirmation before running one; your own tooling should guard them the same
way.
These are the categories that carry x-irreversible: true:
| Category | Examples | Scope |
|---|---|---|
| Deletes (single) | invoices.delete, products.delete, taxes.delete, webhook_endpoints.delete… | <resource>:delete |
| Catalog subresource deletes | 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 |
| Bulk deletes | invoices.bulk_delete, products.bulk_delete… | <resource>:delete |
| Fiscal emission / numbering | invoices.send, invoices.mark_sent, invoices.assign_real_number, invoices.corrective, invoices.substitute_simplified | invoices:send / invoices:write |
| Void / annul | invoices.void, invoices.annul | invoices:void |
| Terminal conversions | quotes.convert, proformas.convert, delivery_notes.convert | <resource>:transition |
| Cancel / sign | 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 / certificate | webhook_endpoints.rotate_secret, verifactu.certificates.revoke | webhooks:write / verifactu:write |
| GDPR forget | delivery_notes.signature_audits.forget | delivery_notes:gdpr_forget |
| FacturaE (B2G) submit | invoices.face_submissions.submit, face_submissions.cancel | facturae:write |
| Time-record seal | monthly_time_record_closes.seal | time_entries:write |
| Replay (re-execution) | automations.runs.replay, automations.runs.steps.replay, integrations.events.replay | automations:write / integration_events:write |
This list is the human-readable summary. The machine-readable source of
truth is x-irreversible in the spec — a client that reads it stays correct
even as the catalog grows.
Putting it together
A safe client does two checks before a mutation:
- Scope-check — does the key hold
x-required-scope? If not, stop locally (no wasted round trip). The CLI exits4; see scope-check. - Irreversibility confirm — is
x-irreversibletrue? If so, confirm before calling. The CLI requires--confirm <id>; see irreversible operations.
The official SDKs and the CLI do both for you. If you generate your own client from the spec, wire these two checks yourself from the extensions.
Store permissions
Store management uses stores:read and stores:write; provider-specific scopes are woocommerce_store:read, woocommerce_store:write, shopify_store:read and shopify_store:write. Connection-test operations require the provider write scope. These scopes are assigned to API keys and are outside OAuth consent. See ecommerce stores for the operation matrix.