Ownership errors
Ownership refusals are business errors. They never come back as HTTP 401 or 403; those statuses mean your token or scope is wrong, which is a different problem (see Responses and error codes). The same underlying code is presented two ways depending on the route family you called.
How a code surfaces
Section titled “How a code surfaces”| Route family | Shape on failure |
|---|---|
Legacy (/api/legacy/provider/**, /api/legacy/agent/**) |
HTTP 200, { "code": "0012", "desc": "<message>" }. The BankCore code is not in the body; only the message distinguishes causes. |
| Find agent by phone (A24) | HTTP 404, { "code": "0012", "desc": "Agent not found" }. |
Agent KYC verification routes (/api/legacy/agent/kyc/*) |
HTTP 400, { "status": "error", "message": "<message>", "data": null }. |
BankCore-native (/api/nip-transfers, /api/bill-payments, /api/transactions/*) |
HTTP 400, { "timestamp", "errorCode": "<CODE>", "message", "path", "status": 400 }. |
Because every ownership code collapses to 0012 on legacy routes, match on desc when you need to tell them apart. The messages below are the exact server strings, with the account number substituted.
The codes
Section titled “The codes”PARTNER_ACCOUNT_FORBIDDEN
Section titled “PARTNER_ACCOUNT_FORBIDDEN”Legacy 0012. Two messages:
Account 6020012345 is not linked to this API client. The account is neither your own customer’s nor a sub-agent’s you can see. Either you named the wrong account, the agent was not onboarded through your credentials, or the bank has switched sub-agent access off for your client. Check with agent details or the API access page.Account 0000123456 belongs to a sub-agent; debiting it requires the sub-agents:debit scope. You can read the agent but may not spend its money. Ask the bank for the scope if your agreement with the sub-agent covers it; see Acting on a sub-account’s money.
Returned by balance, statement, transaction list, identity update, agent details, downline balance, the agent document upload, and every debit path.
PARTNER_SUBAGENT_FORBIDDEN
Section titled “PARTNER_SUBAGENT_FORBIDDEN”Legacy 0012. Three messages:
This API client does not have sub-agent access enabled, from downlines and list agents. Rather than an empty list you get a refusal, so “none” and “not allowed” stay distinguishable. Ask the bank to enable sub-agent access.This API client is not permitted to onboard agents, from add agent, when the service-level check finds nosub-agents:create. (The route filter normally refuses first with HTTP 403SCOPE_DENIED.)This API client is not linked to a customer and cannot onboard agents, from add agent, for a misconfigured client. Contact the bank; nothing you send will change it.
BVN_WATCHLISTED
Section titled “BVN_WATCHLISTED”Legacy 0012. Message BVN is on the NIBSS watchlist (category <n> - <description>, until <date>) or BVN is on the NIBSS deceased list. Raised at agent onboarding, agent identity update and tier upgrade. Nothing is created or changed. There is no override on the API; refer the person to the bank.
Not-found answers
Section titled “Not-found answers”The guard never answers for an account that does not exist; the endpoint’s own not-found handling does, so a refused number and a non-existent one are never confused:
| Where | Answer |
|---|---|
Provider money routes (/wallets/transactions/*) |
0012 or 0096, Account not found: 6020099999 (the page for each route says which) |
| Agent routes | 0012, Agent account not found: 6020099999 or Downline account not found: ... |
| Find agent by phone | HTTP 404, 0012, Agent not found (also used for an agent outside your estate) |
| Native routes | HTTP 404 with errorCode ACCOUNT_NOT_FOUND |
Header and shape errors you will meet alongside
Section titled “Header and shape errors you will meet alongside”| Legacy code | desc |
When |
|---|---|---|
0096 |
X-Agent-Account header is required |
Agent route called without the header. |
0096 |
Account does not belong to an agent |
The header names an account whose owner is neither an agent nor a corporate. |
0012 |
Missing required header: X-Agent-Account |
Agent KYC route called without the header. |
0014 |
A customer with this BVN already exists, An agent with this NIN already exists, Agent with this phone number already exists |
Duplicate identity on create or update. |
0062 |
varies | The account is locked, frozen, under PND or closed. Not an ownership error. |
Scope and IP refusals, for contrast
Section titled “Scope and IP refusals, for contrast”| HTTP | Body | Meaning |
|---|---|---|
| 403 | { "code": "SCOPE_DENIED", "message": "Insufficient scope: <scope>" } |
Your client lacks the scope for the route. On add agent the message reads agents:create or sub-agents:create. |
| 403 | { "code": "IP_NOT_ALLOWED", ... } |
Source address not on your allowlist. |
| 401 | Token expired or revoked. Get a new one and retry once. |
These are decided before your request reaches the service, so an ownership check never runs on them.
What to do, by situation
Section titled “What to do, by situation”| You see | Do |
|---|---|
0012 with not linked to this API client on an agent you onboarded |
Confirm the agent was created with your credentials (its createdAt and your audit log), then ask the bank whether sub-agent access is on. |
0012 with requires the sub-agents:debit scope |
Decide whether you need spending power. If yes, request the scope; if no, change the flow so the sub-agent initiates the debit from its own application. |
0012 with not linked to this API client on an account that is neither yours nor an agent’s |
Expected. A partner’s reach is its own accounts and the agents and merchants it onboarded, nothing else; see Ownership model. |
0012 with does not have sub-agent access enabled |
Ask the bank to enable it. Until then the per-agent calls fail too. |
| HTTP 404 from find by phone | Treat as “not yours”. Do not infer whether the number is registered. |
0012 with BVN is on the NIBSS ... |
Stop. Do not retry with the NIN alone to get round it; the bank sees both attempts. |
In the portal
Section titled “In the portal”The API access page shows each client’s scopes and whether sub-agent access is enabled, which is enough to explain most refusals on this page.