Scopes
Each API key has an associated list of scopes that define which endpoints it can call. The server verifies the scope required by each endpoint and returns 403 Forbidden if the API key doesn’t have it.
Available scopes
| Scope | What it enables |
|---|---|
facturas:read | GET /facturas/{clave}, GET /facturas/{clave}/pdf, GET /facturas/{clave}/xml, GET /facturas/{clave}/respuesta, GET /consecutivos/proximo |
facturas:write | POST /facturas for FE (01) and TE (04), and POST /facturas/{clave}/email |
notas:write | POST /facturas for NC (03) and ND (02) |
facturas:anular | DELETE /facturas/{clave} |
facturas-compra:write | POST /facturas-compra |
facturas-compra:read | GET /facturas-compra/{clave}, GET /facturas-compra/{clave}/pdf, GET /facturas-compra/{clave}/xml, GET /facturas-compra/{clave}/respuesta |
catalogo:write | POST /productos, POST /clientes, POST /marcas |
catalogo:read | GET /productos/{codigo}, GET /clientes/{identificacion}, GET /catalogos/* |
mensajes:write | POST /mensajes-receptor (accept or reject a supplier’s invoice) |
mensajes:read | GET /mensajes-receptor/{clave} |
Incompatible change since v1.2: issuing a NC or ND is no longer enabled by
facturas:write. A key with onlyfacturas:writereceives403when sendingtipo: "NotaCredito"or"NotaDebito". See the changelog.
Why NC/ND have their own scope
POST /facturas is a single endpoint, but depending on the tipo it does two very different things:
- FE/TE: registers a new sale. It’s the everyday operation.
- NC/ND: modifies a document that has already been issued — typically returning money (NC) or charging the customer an additional amount (ND) on a document already submitted to Hacienda. It’s a more sensitive permission: a compromised key that only issues documents cannot retroactively void someone else’s sales; one that can also issue NCs, can.
Separating FE from TE, on the other hand, would add no protection at all: they’re the same operation (registering a sale) with or without an identified customer — splitting their scopes would only add friction without gaining security.
facturas-compra:* is a separate pair of scopes because the operation is fundamentally different: the document is issued on behalf of a third party (the supplier), not for one’s own sale. Mixing it with the sales scopes would let a regular POS key unintentionally issue tax documents attributed to a supplier. See Issue a purchase invoice.
mensajes:* is separate because it doesn’t correspond to a sale: accepting or rejecting a supplier’s invoice determines whether that purchase is recorded as an expense, and it’s issued on your own behalf against someone else’s document. A key meant for invoicing shouldn’t be able to answer to the tax authority for the business’s purchases. See Accept or reject a supplier’s invoice.
catalogo:* is also separate, for a different reason: writing here doesn’t issue anything fiscal, but it affects master data — price, CABYS, taxes — that later feeds every document issued with that product. A key meant only for invoicing shouldn’t be able to modify the price or the CABYS code of the business’s catalog. See Create or update a product.
Recommended combinations
| Use case | Scopes |
|---|---|
| E-commerce that only issues tickets to the end consumer | facturas:read, facturas:write |
| POS that issues FE/TE and handles returns | facturas:read, facturas:write, notas:write |
| Issuance + internal voiding + returns | facturas:read, facturas:write, notas:write, facturas:anular |
| Back office for purchases from special-regime suppliers | facturas-compra:read, facturas-compra:write |
| Read-only (audit, accounting) | facturas:read |
| Integration that also syncs its catalog (products/customers) | facturas:read, facturas:write, catalogo:read, catalogo:write |
| Back office that also responds to its suppliers’ invoices | mensajes:read, mensajes:write |
| Full integration (sales + purchases + voiding + catalog + messages) | all ten |
Principle of least privilege: only request the scopes the integration actually needs. An e-commerce site that never issues returns via the API doesn’t need
notas:write, even if it issues hundreds of tickets a day. If additional scopes are needed later, the API key can be rotated with expanded scopes.