Scopes and permissions
What a key is allowed to do, the full scope vocabulary, and the endpoints keys cannot reach at all.
A key's scopes decide what it can do. Scopes are resource:action, for example
invoices:write.
Wildcards
Three forms are accepted:
| Scope | Grants |
|---|---|
* | Everything |
invoices:* | Every action on invoices |
invoices:write | Just that one |
* is the default. A key created without a scopes list holds it. Pass an explicit
list if you want the key restricted.
The full vocabulary
| Resource | Actions |
|---|---|
invoices | read write delete |
customers | read write delete |
inventory | read write delete |
business | read write delete |
integrations | read write delete |
webhooks | read write delete |
credit-notes | read write |
payments | read write |
firs | read write |
si | read write |
received-invoices | read |
notifications | read |
audit | read |
stats | read |
storage | read |
profile | read |
Anything outside this list is rejected when you create the key, rather than accepted and then silently useless.
Resources maps each of these to the exact endpoints it unlocks.
firs:write is accepted when you create a key but currently gates no endpoint — every
FIRS reference call is a read. Do not rely on it; use firs:read.
What a scope you might not expect
Some scopes are less obvious than they look:
firs:readcovers the FIRS reference data you need to build an invoice — HS codes, service codes, currencies, tax categories, states and LGAs. An invoice-only key cannot fetch them, and the failure appears at lookup time rather than at creation. If you are building invoice payloads, include it.invoices:writecovers validating, signing, submitting, cancelling and editing line items — not only creating. Filing an invoice needs write access, not read.invoices:deleteis only for deleting an invoice. Removing a line from an invoice is an edit, so it needsinvoices:write.
Endpoints an API key cannot reach
Some endpoints refuse API keys entirely, whatever scopes the key holds. They all change who can act or what they can act with:
- Creating, revealing or revoking API keys
- Creating, updating, suspending or deleting users
- Creating or editing roles
- Changing a password
- Signing out
- Billing and subscription purchase
These need a signed-in session. A scope cannot substitute, because the default *
wildcard would satisfy any scope we put on them — so the restriction has to be absolute.
When a scope is missing
You get 403 with the scope named:
{
"statusCode": 403,
"message": "Insufficient permissions. Required scopes: invoices:write",
"error": "Forbidden",
"timestamp": "2026-09-02T14:29:56.000Z",
"path": "/api/v1/invoices"
}Read the message — it tells you exactly which scope to add. You will need to create a
replacement key, since scopes cannot be changed on an existing one.