Authentication
BankCore uses OAuth2 client credentials. The bank issues your organisation a client_id and a client_secret when your partner API client is approved. You exchange them for a bearer token, and you send that token on every other call.
The token carries everything the platform needs to know about you: which tenant you belong to, which scopes you hold, which customer record you act for, and where your fees are charged. Nothing you put in a request body can widen any of that.
Request
Section titled “Request”No Authorization header. The body is JSON.
| Field | Type | Required | Description |
|---|---|---|---|
client_id |
string | Yes | Your client id, for example pa_ab12cd34. Shown on the API access page of the partner portal. |
client_secret |
string | Yes | The secret issued with it. Never shown again after issue. |
curl -X POST https://api.bankcore.ng/api/auth/channel/token \ -H "Content-Type: application/json" \ -d '{ "client_id": "pa_ab12cd34", "client_secret": "sk_live_xxxxxxxxxxxxxxxxxxxx" }'{ "client_id": "pa_ab12cd34", "client_secret": "sk_live_xxxxxxxxxxxxxxxxxxxx"}Response
Section titled “Response”{ "access_token": "eyJhbGciOiJIUzI1NiJ9.eyJjbGllbnRJZCI6InBhX2FiMTJjZDM0Iiwi...", "token_type": "Bearer", "expires_in": 1800, "channel": "API", "client_name": "Acme Payments Production"}| Field | Description |
|---|---|
access_token |
A signed JWT. Send it as Authorization: Bearer <access_token>. |
token_type |
Always Bearer. |
expires_in |
Seconds until the token stops working. Read this value rather than assuming it; the bank can change it. The default is 30 minutes. |
channel |
The channel your client is tagged with. Every customer and transaction you create carries it. |
client_name |
The display name the bank gave your client. |
Errors
Section titled “Errors”The token endpoint is a BankCore-native endpoint, so failures come back as real HTTP statuses with a structured body.
| HTTP | errorCode |
When |
|---|---|---|
| 401 | INVALID_CLIENT |
Unknown client_id, or the secret does not match the current secret or the previous one inside its grace period. The message is the same in both cases on purpose. |
| 403 | CLIENT_SUSPENDED, CLIENT_REVOKED, CLIENT_PENDING_APPROVAL |
The client exists but is not active. Contact your relationship manager. |
| 403 | IP_NOT_ALLOWED |
The request came from an address outside your IP allowlist. See IP allowlist and rate limits. |
Token lifecycle
Section titled “Token lifecycle”- Expiry. A call made with an expired token is refused with HTTP 401. Treat any 401 as “get a new token and retry once”.
- Revocation. If the bank suspends or revokes your client, tokens already issued stop working at the next request, not at expiry.
- Secret rotation. When your secret is rotated, the previous secret keeps working for a grace period so you can roll the new one out without downtime. After the grace period only the new secret is accepted. Rotation is requested through your relationship manager and the new secret is handed over out of band; it is never returned by any endpoint.
- One token, one client. The token is bound to the
client_idthat requested it. If your organisation holds more than one client, each needs its own token.
What is in the token
Section titled “What is in the token”You never need to decode the token, but it helps to know what it carries, because it explains what the platform will and will not let you do.
| Claim | Meaning |
|---|---|
clientId |
Your client_id. |
clientType |
PARTNER. This is what turns on the ownership guard described in Ownership model. |
customerId |
The corporate customer record you act for. Every account you can reach is tied to it. |
tenantId |
The bank tenant you belong to. Requests are pinned to it regardless of any header you send. |
scopes |
The scopes your client holds. See Scopes. |
settlementAccountId |
The account that funds and receives your wallet funding and charging. |
feeAccountId |
The account your API fees are debited from. |
In the portal
Section titled “In the portal”The API access page lists every client your organisation holds, with its client_id, status, scopes, rate limits, IP allowlist and the time and address of its last use. It never shows a secret.