v1.0REST API

Viscount Payment APIs

Viscount Payment APIs provide a secure, scalable, and efficient foundation for businesses looking to modernise their payment infrastructure. They enable organisations to seamlessly collect, process, and manage payments while maintaining full control over financial operations — from virtual account creation and direct debit mandates to real-time money transfers across Nigerian banks.

Through easy integration, Viscount APIs allow payment capabilities to be embedded directly into your applications, delivering a smooth and reliable user experience. Designed for both startups and established enterprises, the platform offers a dependable, CBN-compliant foundation for scaling payment operations backed by a robust infrastructure and dedicated support team.

Base URLhttps://viscountmfb.app/public/api/app/corporate-api/v1

Getting Started

To integrate Viscount Payment APIs, you must complete a simple onboarding process to ensure compliance and secure access. This process involves creating a corporate account and completing Know Your Customer (KYC) verification before API credentials are issued.

01

Create a Corporate Account

Register your business through the corporate portal at corporate.viscountbank.com by providing accurate company details, including your business registration information, contact details, and authorised signatories. This establishes your organisation as an authorised entity on the Viscount platform and enables access to API credential management.

02

Complete KYC Verification

Submit the required documents for KYC verification, including your Certificate of Incorporation, board resolutions, identification documents for directors, and proof of business address. Once approved, your account will be activated and you will gain access to both the Sandbox (testing) and Live (production) environments along with your API credentials — separate Secret Keys for each environment.

API Environments

Viscount provides two distinct environments to support a robust development lifecycle. This separation ensures your integration is stable and thoroughly tested before processing real financial transactions in production.

Sandbox (Test)

The Sandbox environment is a fully functional replica of the Live environment that allows you to simulate all API features without any real financial impact. You can test complete payment flows, generate mock virtual account details, simulate transfers and direct debits, trigger specific error scenarios, and validate your webhook integrations. All responses mirror the exact format you will encounter in production, making the transition seamless. Sandbox API keys are issued separately from Live keys and are clearly identified in your Corporate Internet Banking portal.

Live (Production)

The Live environment is where real transactions are processed against actual bank accounts. Once you have completed thorough testing in the Sandbox and are confident that your integration handles all scenarios correctly — including error handling, webhook processing, and idempotency — you can switch to Live. All payments collected, transfers sent, and direct debits processed in this environment involve real funds and are subject to Central Bank of Nigeria regulatory requirements.

Security & Authentication

The Viscount Payment API employs a multi-layered authentication and validation framework designed to ensure the security, integrity, and reliability of all transactions. Every API request must satisfy all three security layers before it is processed.

🔑

Secret Key Authentication

All API requests must include a valid Secret Key in the Secret-Key request header. The Secret Key serves as the primary method of identifying and authorising API clients. Keys are issued through the Viscount Corporate Internet Banking portal and are unique to each client application. Separate keys are provided for Sandbox and Live environments — you must ensure the correct key is used for each environment. Secret Keys should be treated as highly sensitive credentials and stored securely on your server. Never expose them in client-side code, version control systems, or public repositories.

🛡️

IP Authorisation

In addition to Secret Key authentication, Viscount enforces IP whitelisting across all environments. Only requests originating from pre-registered server IP addresses are accepted by the API. Any request from an unregistered IP address is automatically rejected with an appropriate error response. This significantly reduces the risk of unauthorised access, even in the event that your Secret Key is compromised. To register or update your whitelisted IPs, use the Corporate Internet Banking portal or contact your Viscount account manager.

🔄

Idempotency Control

Each API request must include a unique Request Key in the Request-Key header. This key enables idempotency control, ensuring that duplicate requests — whether caused by network timeouts, client retries, or connectivity issues — are processed only once. If the API receives a request with a Request Key that has already been used, it will return the original response without processing the request again. This prevents duplicate transactions and maintains data consistency across all operations. See the next section for details on constructing valid Request Keys.

Request Key Generation

The Request Key is a unique identifier included in every API request to enable idempotency control. It is constructed by concatenating five segments, producing a 99-character string that is virtually impossible to duplicate across requests.

Key Structure

SegmentLengthDescription
Timestamp14 charactersCurrent date and time formatted as YYYYMMDDHHmmss. Anchors the key to a specific moment in time.
Number Block28 charactersSeven randomly generated integers summing exactly to 1,000, each zero-padded to 4 digits.
Alpha Block 17 charactersSeven randomly selected uppercase ASCII letters (A–Z).
Digit Block30 charactersThirty randomly generated single digits (0–9).
Alpha Block 220 charactersTwenty randomly selected uppercase ASCII letters (A–Z).

Each Request Key must be unique. Reusing a key triggers the idempotency mechanism and returns the cached response from the original request.

Implementation — PHP

function generateRequestKey(): string
{
     = date('YmdHis');
     = [];
     = 1000;
    for ( = 0;  < 6; ++) {
         = min( - (6 - ), 9999);
         = rand(1, );
        [] = ;
         -= ;
    }
    [] = ;
     = implode('', array_map(
        fn() => str_pad(, 4, '0', STR_PAD_LEFT), 
    ));
     = '';
    for ( = 0;  < 7; ++)  .= chr(rand(65, 90));
     = '';
    for ( = 0;  < 30; ++)  .= rand(0, 9);
     = '';
    for ( = 0;  < 20; ++)  .= chr(rand(65, 90));
    return  .  .  .  . ;
}

Implementation — JavaScript

function generateRequestKey() {
    const now = new Date();
    const pad = (n, l = 2) => String(n).padStart(l, '0');
    const date = now.getFullYear()
        + pad(now.getMonth() + 1) + pad(now.getDate())
        + pad(now.getHours()) + pad(now.getMinutes())
        + pad(now.getSeconds());
    const numbers = [];
    let remaining = 1000;
    for (let i = 0; i < 6; i++) {
        const max = Math.min(remaining - (6 - i), 9999);
        const n = Math.floor(Math.random() * max) + 1;
        numbers.push(n);
        remaining -= n;
    }
    numbers.push(remaining);
    const numBlock = numbers.map(n => String(n).padStart(4, '0')).join('');
    const randChar = () => String.fromCharCode(65 + Math.floor(Math.random() * 26));
    const alpha1 = Array.from({ length: 7 }, randChar).join('');
    const digits = Array.from({ length: 30 }, () => Math.floor(Math.random() * 10)).join('');
    const alpha2 = Array.from({ length: 20 }, randChar).join('');
    return date + numBlock + alpha1 + digits + alpha2;
}

Collecting Payment

Viscount enables you to collect payments through two primary channels: Bank Transfers (via virtual accounts) and Direct Debits. Both channels are accessible through the API, with real-time transaction notifications delivered via webhooks.

Bank Transfers

Generate dedicated virtual accounts for your customers to make payments directly into your Viscount account.

Static Virtual Accounts are permanent accounts assigned to individual customers. They can be used repeatedly for multiple transactions, making them ideal for recurring payments, subscription billing, and long-term customer relationships.

Dynamic Virtual Accounts are single-use accounts generated for a specific transaction. Once payment is received, the account is automatically closed. Suited for one-off payments such as invoice settlements, e-commerce checkout flows, and payment links.

Direct Debits

Debit a customer’s bank account directly based on an authorised mandate.

One-Time Mandates authorise a single debit of a specified amount. The mandate expires automatically once the transaction is completed.

Recurring Mandates authorise repeated debits over a defined period with a set amount per debit. Ideal for loan repayments, subscription billing, instalment payments, and scheduled collections where the customer has granted standing authorisation.

Making Payments

The Viscount Payment API enables you to send money to any bank account in Nigeria — whether to another Viscount account (internal transfer) or to any other commercial bank (external transfer via NIBSS).

Single Transfers allow you to initiate individual payments in real time. Each transfer is processed instantly and assigned a unique reference for tracking. You can optionally flag a transfer as requiring approval before processing, enabling a maker-checker workflow.

Bulk Transfers allow you to send payments to multiple recipients in a single API call by creating a transfer batch. Ideal for salary disbursements, vendor payments, and commission payouts. Each transfer within a batch is tracked individually, and you can approve or cancel an entire batch before processing. All transfers support full verification, real-time status tracking, and webhook notifications upon completion.

Response Format

The Viscount Payment API returns consistent, predictable JSON responses. There are two possible HTTP status codes, each with a defined structure.

200Success

Returns status (always "success"), message, and data (the response payload).

{
  "status": "success",
  "message": "Successful",
  "data": {
    "reference": "VST20260411143200",
    "transaction_id": "TXN_0012345",
    "amount": 50000
  }
}
400Error

Returns only status (always "error") and message. No data field.

{
  "status": "error",
  "message": "Invalid source account number."
}

Webhooks

Webhooks allow Viscount to notify your application in real time when events occur on your account. Rather than polling the API for status updates, your server receives an immediate POST notification.

Configure your webhook URL through the Corporate Internet Banking portal. When an event occurs, Viscount sends a POST request with a JSON body containing the event type, associated data, and a timestamp.

Supported Events

EventDescription
transaction-newA successful credit is received on any account, including virtual accounts. Use this for real-time payment reconciliation.
transfer-successfulAn outbound money transfer has completed successfully. Use this to confirm funds delivery.

Example Payloads

Successful Credit

{
  "event_type": "transaction-new",
  "data": {
    "transaction_id": "TXN_0012345",
    "account_number": "82791089010",
    "reference": "de92ui22",
    "transaction_type": "CREDIT"
  },
  "timestamp": "2026-04-11T14:23:45+00:00"
}

Successful Transfer

{
  "event_type": "transfer-successful",
  "data": { "reference": "de92ui22" },
  "timestamp": "2026-04-11T14:23:45+00:00"
}

Webhook Security

Set a secret hash stored as an environment variable. Viscount signs each payload with HMAC-SHA256 using this hash. The signature is included in the X-Viscount-Signature header. Compute the hash of the raw body and compare.

Verification — Python (Flask)

import hmac, hashlib, os

secret = os.environ['VISCOUNT_WEBHOOK_SECRET']
payload = request.get_data()
signature = request.headers.get('X-Viscount-Signature', '')
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()

if not hmac.compare_digest(expected, signature):
    abort(401)

event = request.get_json()

Verification — Node.js (Express)

const crypto = require('crypto');
const secret = process.env.VISCOUNT_WEBHOOK_SECRET;
const signature = req.headers['x-viscount-signature'];
const expected = crypto.createHmac('sha256', secret)
    .update(req.rawBody).digest('hex');

if (signature !== expected)
    return res.status(401).send('Invalid signature');

const event = JSON.parse(req.rawBody);
Best Practice: Return 200 immediately to acknowledge receipt. Process time-consuming logic asynchronously. Always verify using the raw request body — re-encoding JSON may alter key ordering.
🚀

Try the API in Postman

All endpoints are documented and ready to test. Import the collection with one click — pre-configured headers, example payloads, and environment variables included.

Open Postman Collection →

Virtual Accounts

Create, update, and query virtual accounts for payment collection. All requests are POST to /virtual-accounts.

POST/virtual-accountsCreate Virtual Account

Creates a new virtual account under your corporate account. The account name you provide will be prefixed with your registered business name automatically.

Headers
Request-KeyUnique per request
Secret-KeyYour API secret key
Body Parameters
ParameterDescription
actionstringFixed: CREATE_ACCOUNT
parent_account_numberstringBusiness account number. For dynamic accounts, this is the settlement account.
account_namestringAccount name. Business prefix appended automatically.
account_type_codestringVIRTUAL_ACCOUNT (static) or VIRTUAL_ACCOUNT_DYNAMIC (single-use).
Example
{ "action": "CREATE_ACCOUNT", "parent_account_number": "1000211487", "account_name": "ADEDEJI AWOLOLA", "account_type_code": "VIRTUAL_ACCOUNT" }
POST/virtual-accountsUpdate Virtual Account

Activate or deactivate an existing virtual account.

Headers
Request-KeyUnique per request
Secret-KeyYour API secret key
Body Parameters
ParameterDescription
actionstringFixed: UPDATE_ACCOUNT_NUMBER
account_numberstringThe virtual account number to update.
statusstringACTIVE or INACTIVE.
Example
{ "action": "UPDATE_ACCOUNT_NUMBER", "account_number": "1000211576", "status": "INACTIVE" }
POST/virtual-accountsAccount Enquiry

Retrieve account information and transaction history for a date range.

Headers
Request-KeyUnique per request
Secret-KeyYour API secret key
Body Parameters
ParameterDescription
actionstringFixed: ACCOUNT_ENQUIRY
account_numberstringAccount number to query.
start_datestringStart date (YYYY-MM-DD).
end_datestringEnd date (YYYY-MM-DD).
Example
{ "action": "ACCOUNT_ENQUIRY", "account_number": "1000211576", "start_date": "2026-01-01", "end_date": "2026-01-31" }

Transactions

Verify the status and details of any transaction. Essential for confirming payments, resolving disputes, and maintaining records.

POST/transactionsVerify Transaction

Verifies a transaction using its ID, reference, or both.

Headers
Request-KeyUnique per request
Secret-KeyYour API secret key
Body Parameters
ParameterDescription
actionstringFixed: VERIFY_TRANSACTION
account_numberstringAccount associated with the transaction.
transaction_idstringTransaction ID assigned by Viscount.
referencestringTransaction reference.
Example
{ "action": "VERIFY_TRANSACTION", "account_number": "1000211576", "transaction_id": "20260402195705CDLCCWDWKJ", "reference": "000966444324566658283981389895" }

Money Transfer

Name lookups, single transfers, batch payments, and transfer status tracking. All requests are POST to /money-transfer.

POST/money-transferName Enquiry

Look up an account holder name before transferring.

Headers
Request-KeyUnique per request
Secret-KeyYour API secret key
Body Parameters
ParameterDescription
actionstringFixed: NAME_ENQUIRY
account_numberstringAccount to look up.
account_typestringVISCOUNT or OTHER_BANKS.
bank_codestringCBN bank code (required for OTHER_BANKS).
Example
{ "action": "NAME_ENQUIRY", "account_number": "1000211487", "account_type": "VISCOUNT", "bank_code": "00057" }
POST/money-transferSend Money (Single)

Initiate a single real-time transfer to any Nigerian bank account.

Headers
Request-KeyUnique per request
Secret-KeyYour API secret key
Body Parameters
ParameterDescription
actionstringFixed: SEND_MONEY
account_numberstringDestination account.
account_typestringVISCOUNT or OTHER_BANKS.
bank_codestringCBN bank code (for OTHER_BANKS).
amountnumberAmount in Naira.
subject_to_approvalstringYES or NO.
from_account_numberstringSource account to debit.
referencestringUnique transaction reference.
narrationstringDescription or note.
Example
{ "action": "SEND_MONEY", "account_number": "1000211521", "account_type": "VISCOUNT", "amount": 1000, "subject_to_approval": "NO", "from_account_number": "1000211487", "reference": "678986568909MMKLOdPP", "narration": "Vendor payment" }
POST/money-transferCreate Transfer Batch

Create a batch of transfers from a single source account. Must be approved before processing.

Headers
Request-KeyUnique per request
Secret-KeyYour API secret key
Body Parameters
ParameterDescription
actionstringFixed: CREATE_TRANSFER_BATCH
from_account_numberstringSource account for all transfers.
batch_titlestringDescriptive batch title.
transfersarrayArray of transfer objects with account_number, type, amount, narration, bank_code.
Example
{ "action": "CREATE_TRANSFER_BATCH", "from_account_number": "1000211487", "batch_title": "SALARY PAYMENT", "transfers": [{ "account_number": "1000211497", "type": "VISCOUNT", "amount": 8000, "narration": "SALARY", "bank_code": "" }] }
POST/money-transferApprove Transfer Batch

Approve a pending batch for processing.

Headers
Request-KeyUnique per request
Secret-KeyYour API secret key
Body Parameters
ParameterDescription
actionstringFixed: APPROVE_TRANSFER_BATCH
batch_idstringBatch ID to approve.
Example
{ "action": "APPROVE_TRANSFER_BATCH", "batch_id": "051126260411202537782393912540" }
POST/money-transferCancel Transfer Batch

Cancel a pending batch before approval.

Headers
Request-KeyUnique per request
Secret-KeyYour API secret key
Body Parameters
ParameterDescription
actionstringFixed: CANCEL_TRANSFER_BATCH
batch_idstringBatch ID to cancel.
Example
{ "action": "CANCEL_TRANSFER_BATCH", "batch_id": "051126260411202537782393912540" }
POST/money-transferGet Batch

Retrieve batch details and individual transfer statuses.

Headers
Request-KeyUnique per request
Secret-KeyYour API secret key
Body Parameters
ParameterDescription
actionstringFixed: GET_TRANSFER_BATCH
batch_idstringBatch ID to retrieve.
Example
{ "action": "GET_TRANSFER_BATCH", "batch_id": "051126260411205427287611731074" }
POST/money-transferTransfer Status

Check the processing status of a specific transfer by its reference.

Headers
Request-KeyUnique per request
Secret-KeyYour API secret key
Body Parameters
ParameterDescription
actionstringFixed: TRANSFER_STATUS
referencestringTransfer reference to look up.
Example
{ "action": "TRANSFER_STATUS", "reference": "051126260411205427515805070443" }

Direct Debits

Create mandates, execute debits, and manage mandate lifecycle. All requests are POST to /direct-debit.

POST/direct-debitCreate Direct Debit

Initiate a new direct debit mandate — one-time or recurring.

Headers
Request-KeyUnique per request
Secret-KeyYour API secret key
Body Parameters
ParameterDescription
actionstringFixed: INITIATE
account_numberstringAccount to be debited.
narrationstringMandate description.
amountnumberDebit amount per transaction (Naira).
account_typestringVISCOUNT or OTHER_BANKS.
settlement_account_numberstringViscount account for settlement.
bank_codestringBank code (for OTHER_BANKS).
typestringONE_TIME or RECURRING.
start_datestringStart date for RECURRING (YYYY-MM-DD).
end_datestringEnd date for RECURRING (YYYY-MM-DD).
Example
{ "action": "INITIATE", "account_number": "1000211487", "narration": "Monthly subscription", "amount": 5000, "account_type": "VISCOUNT", "settlement_account_number": "1000211545", "bank_code": "", "type": "ONE_TIME", "start_date": "", "end_date": "" }
POST/direct-debitDebit Mandate

Execute a debit against an existing active mandate.

Headers
Request-KeyUnique per request
Secret-KeyYour API secret key
Body Parameters
ParameterDescription
actionstringFixed: DEBIT_MANDATE
mandate_idstringMandate ID to debit.
amountnumberAmount to debit (Naira).
Example
{ "action": "DEBIT_MANDATE", "mandate_id": "1000211487", "amount": 5000 }
POST/direct-debitCancel Mandate

Cancel an active mandate, permanently revoking debit authorisation.

Headers
Request-KeyUnique per request
Secret-KeyYour API secret key
Body Parameters
ParameterDescription
actionstringFixed: CANCEL_MANDATE
mandate_idstringMandate ID to cancel.
Example
{ "action": "CANCEL_MANDATE", "mandate_id": "1000211487" }
📬

Ready to Integrate?

Import the complete Viscount API collection into Postman to start testing all endpoints with pre-configured headers, example payloads, and environment variables.

Open Postman Collection →