Skip to content

Scopes

A scope is a string of the form resource:action, for example transactions:transfer or virtual-accounts:read. Your client holds a list of them, the list is embedded in every token you are issued, and every route in the API is mapped to the scope it requires. The check runs before your request reaches any business logic.

Scopes say what kinds of operation you may perform. They say nothing about whose accounts you may perform them on; that is the ownership guard, described in Ownership model. You need to pass both.

A scope of the form resource:* satisfies any requirement on that resource: a client holding transactions:* passes a route that needs transactions:deposit, transactions:read or transactions:web-credit. There is no wildcard on the action side (*:read is not a thing), and the global *:* exists for the bank’s own servers but can never be issued to a partner.

Scopes are compared as exact strings. transaction:transfer (singular) matches nothing; resource names are plural as listed below.

Grouped by resource. The route column is illustrative; the full map is in Endpoint index by scope.

Accounts and virtual accounts

Scope Unlocks
accounts:read Agent balance (GET /api/legacy/agent/balance) and the path-variable reads under /wallets/{accountNumber}/... such as the card routes; listing virtual accounts
accounts:create Creating a dynamic virtual account (POST /api/legacy/agent/virtual_account/create)
virtual-accounts:create Creating a fixed virtual account; expiring a virtual account
virtual-accounts:read Checking whether a virtual account has been paid

Transactions

Scope Unlocks
transactions:deposit Fund an account from your settlement account; deposit by path
transactions:withdraw Charge an account to your settlement account
transactions:transfer Transfer between accounts at the bank; agent mobile transfer; USSD cashout
transactions:web-credit Web-acquiring credit to a merchant. Separate from transactions:deposit on purpose: it credits against a receivable rather than your funded settlement account
transactions:read Statements, transaction lists, check-transaction, USSD status

Interbank

Scope Unlocks
nip:name-enquiry Resolve an account name at any bank
nip:transfer Outward interbank transfer from your own account or an agent’s account

Bill payments

Scope Unlocks
bill-payments:read Biller catalogue, products, fee estimates, subscriber validation
bill-payments:create Pay a bill; buy airtime

Agents and sub-agents

Scope Unlocks
agents:read Agent details, activity profile, find by phone, list, downlines
agents:manage Change PIN, move income, update an agent’s BVN/NIN
sub-agents:create Onboard an agent or merchant parented to you (POST /api/legacy/agent/admin/add_agent)
sub-agents:debit Move money out of a sub-agent’s accounts (see below)

KYC

Scope Unlocks
kyc:upload Upload documents and verification results against an agent or merchant in your estate
kyc:read Read-side KYC scope. No agent route uses it today; see KYC uploads

POS

Scope Unlocks
pos:cashout mPOS and third-party terminal cashout
pos:bill-pay mPOS bill payment
pos:read Read POS transactions

The bank’s system refuses to create or amend a partner client with any of these, whoever asks:

Scope Why
*:* Everything, tenant-wide
agents:*, agents:create Tenant-wide agent creation and terminal mapping. A partner onboards agents with sub-agents:create, which only ever produces agents parented to that partner
kyc:*, kyc:manage Placing and lifting post-no-debit restrictions and tier upgrades are compliance decisions made at the bank

You can hold agents:read and agents:manage: they are bounded by the ownership guard to your own profile and your sub-agents.

Reaching a sub-agent’s money takes two separate grants, deliberately:

  1. Seeing a sub-agent (balance, statement, transaction list, downline listing) is switched on by a flag on your client, sub_agent_access_enabled, that the bank sets. It is read live on every request, so switching it off takes effect immediately rather than when your token expires. It is not a scope and does not appear in your token.
  2. Debiting a sub-agent (charging its account, transferring or paying bills out of it) additionally needs the sub-agents:debit scope. A sub-agent’s account is a different legal person’s money, so a partner that needs only visibility over its estate is not given spending power over it.

Holding sub-agents:debit without the visibility flag gets you nothing; holding the flag without the scope lets you read but not spend. See Acting on a sub-account’s money.

A call to a route whose scope you do not hold is answered with HTTP 403 and this body, whichever path family the route belongs to:

{
"code": "SCOPE_DENIED",
"message": "Insufficient scope: nip:transfer"
}

The message names the scope the route needs. Where a route accepts either of two scopes (agent onboarding accepts agents:create or sub-agents:create) the message lists both joined by “or”.

Two things worth knowing:

  • Routes that are not in the scope map at all (staff-only administration endpoints) are not scope-checked; they are refused further in with ACCESS_DENIED (native) or 0012 (legacy). Holding a wide wildcard does not open them.
  • A refusal is logged on the bank’s side with your client id, the route and the scopes you hold, so your relationship manager can see exactly what was asked for.

Scopes are set by the bank. To add or remove one, ask your relationship manager; the change goes through the same approval as the original request. Because scopes live inside the token, a change takes effect when you next fetch a token, not on your current one. Request a fresh token after the bank confirms the change.

The API access page lists the scopes your client holds.