Virtual cards and POS
Card issuing and POS acquiring are enabled per partner by the bank, on request, and come with their own onboarding: card products, terminal mappings, key injection and the acquirer’s test cycle. This page is an orientation. The field-level reference for everything beyond the two calls shown here, together with test material, is available from the bank once the capability is enabled for your client.
Virtual cards
Section titled “Virtual cards”Virtual Verve cards are issued against a wallet’s customer and operated from the same account number. Two route families exist; they do the same things and differ only in where the account number goes.
Wallet-scoped (account number in the path):
| Operation | Endpoint |
|---|---|
| Create | POST /api/legacy/provider/wallets/{accountNumber}/virtual_card |
| Details | POST …/virtual_card/info |
| Status at the scheme | POST …/virtual_card/status |
| Block / unblock | POST …/virtual_card/block, POST …/virtual_card/unblock |
| PIN reset / change | POST …/virtual_card/pinreset, POST …/virtual_card/pinchange |
Provider-scoped (account number or card reference in the body), the jCard shape:
| Operation | Endpoint |
|---|---|
| Create | POST /api/legacy/provider/virtual_card/create |
| Details | POST /api/legacy/provider/virtual_card/info, GET/POST …/infoByClientReference |
| Status, block, unblock | POST …/status, POST …/block, POST …/unblock |
| PIN reset / change | POST …/reset_pin, POST …/change_pin |
| 3-D Secure enrolment | POST …/enrollSafeToken |
Every call needs the bearer token. Where a card is addressed by account number alone, the most recently issued card that is not closed, expired, lost or stolen is the one acted on; pass cardReference to pick a specific card.
Create a virtual card
Section titled “Create a virtual card”| Field | Type | Required | Description |
|---|---|---|---|
cardProductId |
UUID | Yes | The card product the bank assigned you. |
clientReference |
string | Yes | Your reference. The idempotency key for a safe retry. |
creationChannel |
string | No | Free text recorded on the card. Default API. |
curl -X POST https://api.bankcore.ng/api/legacy/provider/wallets/6020012345/virtual_card \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "cardProductId": "550e8400-e29b-41d4-a716-446655440000", "clientReference": "CARD-REQ-0001" }'{ "responseCode": "00", "responseMessage": "Card created successfully", "data": { "cardId": "b1c2d3e4-0000-0000-0000-000000000001", "maskedPan": "506099******1234", "status": "ACTIVE", "cardType": "VIRTUAL", "cardReference": "VCC-20260401-0001", "clientReference": "CARD-REQ-0001", "accountNumber": "6020012345", "bin": "506099", "lastFour": "1234", "startDate": "2026-04-01", "endDate": "2029-04-01", "enrolled": false, "pan": "5060990000001234", "cvv2": "123", "defaultPin": "0000" }}Card responses are not the legacy code/desc envelope on success: create answers HTTP 201 and the others 200, with responseCode 00. The full PAN, CVV and default PIN are returned on issuance and PIN operations only, and only when the scheme returned them for that call; treat the response as cardholder data and never log it. Failures do use the legacy envelope: a missing cardProductId or clientReference is 0012, an unknown account 0012, and anything unexpected 0096.
The provider-scoped create takes accountNumber, clientReference, optional cardType (only VERVE is accepted) and cardProductId, which may be omitted when your tenant has exactly one active virtual product. Its reply is the flat jCard card object (cardReference, clientReference, accountNumber, pan, cvv, cvv2, createdAt and expiresAt as yy/MM, status booleans), plus defaultPin.
Card activity reaches you as card.authorization and card.reversed webhooks; see Events and payloads.
POS acquiring
Section titled “POS acquiring”POS endpoints are agent endpoints: each call names the agent whose terminal is used with X-Agent-Account, and a sub-agent’s account needs sub-agents:debit.
| Operation | Endpoint | Scope |
|---|---|---|
| mPOS cash-out | POST /api/legacy/agent/transact/mpos/cashout2 |
pos:cashout |
| mPOS bill payment | POST /api/legacy/agent/transact/mpos/cashout2_bills |
pos:bill-pay |
| Third-party terminal (PAX, Ingenico; cash-out, bills, contactless) | POST /api/legacy/agent/transact/pos |
pos:cashout |
| List your POS transactions | GET /api/pos-transactions |
pos:read |
mPOS cash-out
Section titled “mPOS cash-out”| Header | Required | Description |
|---|---|---|
X-Agent-Account |
Yes | The agent’s 10-digit transaction account. |
| Field | Type | Required | Description |
|---|---|---|---|
terminalId |
string | Yes | A terminal mapped to the agent. |
data |
string | Yes | The EMV/ICC TLV data from the card read, as a hex string. The PIN block is inside the TLV. |
total |
string | Yes | Amount in kobo ("500000" is ₦5,000). amount is accepted as an alias, also in kobo; total wins when both are sent. |
mode |
string | No | Entry mode passed through to the acquirer. |
type |
string | No | CASHOUT (default) or BILL_PAY, which routes to the bill-payment handler and needs billerId, productId and subscriberId. |
clientRef |
string | No | Your reference, echoed back. transactionRef and ref are accepted as aliases. |
{ "responseCode": "0000", "responseRef": "KMN-20260401-0001", "merchantRef": "KMN-20260401-0001", "paymentRef": "KMN-20260401-0001", "clientRef": "POS-0001", "description": "Approved", "amount": 5000.00, "fee": 65.00, "agentPortion": 40.00}| Field | Description |
|---|---|
responseCode |
0000 approved, 0009 pending at the acquirer (09, 10, 91 from the switch), 9999 declined or failed. |
responseRef, merchantRef, paymentRef |
The acquirer’s reference, the same value three times for jCard compatibility. Use it for reconciliation and disputes. |
description |
The acquirer’s response message. |
fee, agentPortion |
The issuer fee charged and the agent’s commission. |
token, units, receipt |
Present only on a bill payment against a vending product. |
A request the platform cannot process (missing amount, unknown terminal) answers responseCode 9999 with the reason in description; an unexpected error on cashout2 is an HTTP 500 with the same body. The third-party /transact/pos route takes the acquirer’s structured fields instead (deviceId, sendIccData, field52, pinKsn, 12-digit kobo amount, payType, extra) and replies with a printable receipt object; its reference is in the material the bank provides on enablement.
In the portal
Section titled “In the portal”Card and POS activity is not a separate page. Settled POS cash-outs and bill payments appear on the agent’s statement under Agents and in the Transactions register.