Viscount Payment API · v1

Move money
from your own
code.

Virtual accounts, transfers, bulk disbursements and direct debits — settled through Viscount Microfinance Bank, driven from your application.

17 endpoints across 6 groups. Bearer tokens, JSON in, JSON out.

__PRODHOST__
# Step one, every time
curl -X POST https://viscountmfb.app/public/api/app/corporate-api/v1/get-access-token \
  -H "Content-Type: application/json" \
  -d '{
  "secret_key": "07714571175724834950",
  "api_key": "0C24C27391204354A5FC39772C9BDECEA058F6066BFF5E6C8B2242CBF3AD7762",
  "grant_type": "client_credentials"
}'
# 200 OK
{
  "data": {
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9\u2026"
  },
  "status": "success",
  "message": "Successful"
}
Sandbox. Simulated transactions only. Sandbox credentials are issued separately — and the IP allowlist applies here too.

Six groups. 17 endpoints.

The whole surface, in the order you will meet it.

Required · both environments

IP authorization

The Viscount API only accepts requests from IP addresses you have authorized. This applies in sandbox and in production, without exception.

What is checked

Every request is matched against your business's list of authorized IP addresses before authentication is considered. An unlisted address is refused even when the credentials and bearer token are perfectly valid — the allowlist sits in front of everything else.

Where you configure it

The list lives in your business's Viscount internet banking portal, maintained per environment. Sandbox and production are configured separately, so authorizing one does not authorize the other.

Setting it up

  1. Determine the public egress IP of the machine that will call the API. This is the address the internet sees, not the private address inside your network.
  2. Sign in to your business's Viscount internet banking portal and open your authorized IP settings.
  3. Add the address, and repeat for each environment you intend to call.
  4. If your traffic leaves through a load balancer, NAT gateway, proxy or VPN, authorize every address it can exit from — not just one.
  5. Test from the server itself. A call that succeeds from your laptop proves nothing about your server.

Diagnosing a rejection

The tell is environmental rather than logical: the same request works from one machine and fails from another, or worked in Hoppscotch on your desk and fails once deployed. Before you regenerate keys or re-read the auth flow, confirm the calling server's egress IP is on the list.

Dynamic and elastic infrastructure

Autoscaling groups, container platforms and serverless functions rotate outbound addresses by default, which makes them impossible to allowlist reliably. Route their traffic through a NAT gateway or a static outbound IP first, then authorize that single address.

Keep the list short and deliberate. Every address on it is a machine permitted to move your money, so remove decommissioned servers as carefully as you add new ones.
Throttling

Rate limits

Every response tells you where you stand. Read the headers rather than guessing.

x-ratelimit-limitRequests permitted in the current window. Sample responses from the API return a limit of 30.
x-ratelimit-remainingRequests left in the window. Back off as this approaches zero rather than retrying into the wall.
Two easy savings: cache your access token for its full life instead of minting one per request, and cache the bank list rather than refetching it before every transfer.
Error handling

Errors & status

Every response — success or failure — arrives in the same envelope.

The envelope

Responses carry status, message and, where there is something to return, data. Branch on status, show message to your engineers rather than your customers, and read data only once you have confirmed success.

Do not assume 400

A failed request will not always come back as 400. Inspect the HTTP status code as well as the body, and treat anything outside the 2xx range as unsuccessful — including responses your client library may swallow.

successThe call was accepted. For transfers this does not mean settled — status inside data may still read PENDING.
errorThe call was rejected. message carries the reason, for example Invalid Key or Mandate is not approved.
PENDINGA transaction is in flight. Poll Verify Transactions until it resolves.
ERRORA transaction reached a terminal failure. Reconcile it; do not silently retry with the same reference.
Callbacks

Webhooks

Credits into a virtual account, completed transfers and debited mandates arrive as signed JSON at your HTTPS endpoint.

PayloadPOST · your endpoint
{
  "data": { /* varies by event_type */ },
  "status": "successful",
  "message": "Virtual account credited",
  "event_type": "collection.credit"
}
Signed HMAC-SHA256 in the viscount-signature header.
Your endpoint is public. Recompute the signature with your secret hash and compare — if it does not match, discard the request. Then confirm against Verify Transactions before releasing any value.

Handle each event in this order

  1. Receive the webhook.
  2. Verify the signature.
  3. Run basic validation.
  4. Record or queue the event.
  5. Return 200 OK — anything else is a failed delivery.
  6. Finish the business logic in a background job.

You have 60 seconds. Miss it and the event may be redelivered, so keep processing idempotent — check the reference before you credit a wallet twice.

Chapter 01

Authentication

Exchange your corporate keys for a short-lived bearer token. Every other call carries it.

POST

Get Access Token

https://viscountmfb.app/public/api/app/corporate-api/v1/get-access-token

Tokens are short-lived — the sample above expires an hour after it is issued. Cache it on your server, watch the clock, and request a new one rather than calling this endpoint before every request.

Body

FieldTypeNotes
secret_keystringYour corporate secret key. Server-side only — never ship it to a browser.
api_keystringYour corporate API key, issued alongside the secret key.
grant_typestringAlways client_credentials.
curl -X POST https://viscountmfb.app/public/api/app/corporate-api/v1/get-access-token \
  -H "Content-Type: application/json" \
  -d '{
  "secret_key": "07714571175724834950",
  "api_key": "0C24C27391204354A5FC39772C9BDECEA058F6066BFF5E6C8B2242CBF3AD7762",
  "grant_type": "client_credentials"
}'
200 OKapplication/json
{
  "data": {
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3Zpc2NvdW50bWZiLmFwcC9wdWJsaWMvYXBpL2FwcC9jb3Jwb3JhdGUtYXBpL3YxL2dldC1hY2Nlc3MtdG9rZW4iLCJpYXQiOjE3NzY4MDA0OTEsImV4cCI6MTc3NjgwNDA5MSwibmJmIjoxNzc2ODAwNDkxLCJqdGkiOiJGTzdMUnp0cEVobmQ3S284Iiwic3ViIjoiNCIsInBydiI6IjI1OTYyZDYxMmU0ZTE4ZTVjZDI4YzNlYjRiMTFiMGJhNDZlZmI0MWQifQ.USXwTvIEUl7yAOayi6hNusEMfdaNygeBgLd5qcKfoyQ"
  },
  "status": "success",
  "message": "Successful"
}
400 Errorapplication/json
{
  "status": "error",
  "message": "Invalid Key"
}
Chapter 02

Virtual Accounts

Issue accounts that collect on your behalf, then switch them on and off as your customers come and go.

POST

Create Virtual Account

https://viscountmfb.app/public/api/app/corporate-api/v1/virtual-account-create

A dynamic account is meant for one payment and can be retired after it is funded. A permanent one belongs to a customer for as long as they are yours.

Headers

HeaderInNotes
AuthorizationheaderBearer token from Get Access Token.

Body

FieldTypeNotes
parent_account_numberstringThe funded corporate account the virtual account sits under.
account_namestringName shown on the virtual account and on the payer's transfer screen.
referencenumberYour own unique reference. Use it to reconcile and to keep retries idempotent.
account_type_codestringVIRTUAL_ACCOUNT for a permanent account, VIRTUAL_ACCOUNT_DYNAMIC for a single-use one.
curl -X POST https://viscountmfb.app/public/api/app/corporate-api/v1/virtual-account-create \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "parent_account_number": "5176887565",
  "account_name": "ADEDEJI AWOLOLA",
  "reference": 90909745467,
  "account_type_code": "VIRTUAL_ACCOUNT"
}'
200 OKapplication/json
{
  "status": "success",
  "message": "Successful"
}
POST

Deactivate Virtual Account

https://viscountmfb.app/public/api/app/corporate-api/v1/virtual-account-deactivate

Headers

HeaderInNotes
AuthorizationheaderBearer token from Get Access Token.

Body

FieldTypeNotes
account_numberstringThe account this call acts on.
curl -X POST https://viscountmfb.app/public/api/app/corporate-api/v1/virtual-account-deactivate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "account_number": "3423558619"
}'
200 OKapplication/json
{
  "status": "success",
  "message": "Successful",
  "data": null
}
POST

Activate Virtual Account

https://viscountmfb.app/public/api/app/corporate-api/v1/virtual-account-activate

Headers

HeaderInNotes
AuthorizationheaderBearer token from Get Access Token.

Body

FieldTypeNotes
account_numberstringThe account this call acts on.
curl -X POST https://viscountmfb.app/public/api/app/corporate-api/v1/virtual-account-activate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "account_number": "3423558619"
}'
200 OKapplication/json
{
  "status": "success",
  "message": "Successful",
  "data": null
}
Chapter 03

Transactions

Independently confirm what actually happened before you give a customer value.

POST

Verify Transactions

https://viscountmfb.app/public/api/app/corporate-api/v1/transaction-verify

This is the endpoint that settles arguments. Never give value on a webhook alone — check status, amount and reference here first.

Headers

HeaderInNotes
AuthorizationheaderBearer token from Get Access Token.
Request-KeyheaderSupplied with your credentials.

Body

FieldTypeNotes
referencestringYour own unique reference. Use it to reconcile and to keep retries idempotent.
transaction_idstringViscount's identifier for the transaction, returned when it was created.
account_numberstringThe account this call acts on.
curl -X POST https://viscountmfb.app/public/api/app/corporate-api/v1/transaction-verify \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "reference": "051126260725101323703074367502",
  "transaction_id": "202607251013570EKX8GMNLU",
  "account_number": "5176887565"
}'
200 OKapplication/json
{
  "status": "success",
  "message": "Successful",
  "data": {
    "account_number": "5176887565",
    "amount": "5000.00",
    "transaction_id": "202607251013570EKX8GMNLU",
    "reference": "051126260725101323703074367502",
    "transaction_type": "DEBIT",
    "narration": "JANUARY 2027 SALARY PAYMENT",
    "status": "ERROR",
    "transaction_mode": "EXTERNAL_BANK_TRANSFER",
    "transaction_time": "2026-07-25T09:13:57.000000Z",
    "outward": {
      "destination_bank_code": "000015",
      "destination_bank_name": "Zenith Bank PLC",
      "destination_account_number": "2009703168",
      "provider": "NIP"
    }
  }
}
Chapter 04

Fund Transfer

Single payouts to any Nigerian bank, plus the name enquiry that should precede them.

POST

Fund Transfer

Moves money

https://viscountmfb.app/public/api/app/corporate-api/v1/transfer-send-money

Run Validate Account first and carry its session_id across as name_enquiry_reference. A transfer without a completed name enquiry is a transfer you cannot defend later.

Headers

HeaderInNotes
AuthorizationheaderBearer token from Get Access Token.

Body

FieldTypeNotes
bank_codestringDestination bank code. Read it from Bank List rather than hard-coding.
bank_typestringOTHER_BANKS routes over NIP. VISCOUNT settles internally and instantly.
account_numberstringThe account this call acts on.
narrationstringText that lands on both statements.
amountnumberAmount in naira.
beneficiary_account_namestringName returned by Validate Account. Do not type it by hand.
beneficiary_kyc_levelstringKYC tier returned by Validate Account.
beneficiary_bvnstringBVN returned by Validate Account.
longitudestringLongitude of the request origin, for transaction monitoring.
latitudestringLatitude of the request origin, for transaction monitoring.
referencestringYour own unique reference. Use it to reconcile and to keep retries idempotent.
from_account_numberstringThe account to debit.
name_enquiry_referencestringThe session_id from Validate Account. Ties this transfer to a completed name enquiry.
subject_to_approvalstringYES parks the transfer in your approval queue instead of sending it.
curl -X POST https://viscountmfb.app/public/api/app/corporate-api/v1/transfer-send-money \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "bank_code": "000015",
  "bank_type": "OTHER_BANKS",
  "account_number": "2009703168",
  "narration": "TESTING BANK TRANSFER FROM API",
  "amount": 1000,
  "beneficiary_account_name": "ADEDEJI AWOLOLA",
  "beneficiary_kyc_level": "3",
  "beneficiary_bvn": "22000000052",
  "longitude": "3.35159000",
  "latitude": "6.60547000",
  "reference": "765234567890098758899",
  "from_account_number": "5176887565",
  "name_enquiry_reference": "999999260725085714422300532528",
  "subject_to_approval": "NO"
}'
200 OKapplication/json
{
  "status": "success",
  "message": "Transaction posted successfully",
  "data": {
    "reference": "765234567890098758899",
    "transaction_id": "20260726103127UBCHDEZV0Z",
    "status": "PENDING"
  }
}
GET

Bank List

https://viscountmfb.app/public/api/app/corporate-api/v1/transfer-bank-list

Cache this. It changes rarely, and refetching it before every transfer wastes a call against your rate limit.

Headers

HeaderInNotes
AuthorizationheaderBearer token from Get Access Token.
curl https://viscountmfb.app/public/api/app/corporate-api/v1/transfer-bank-list \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json"
200 OKapplication/json
{
  "status": "success",
  "message": "Successful",
  "data": [
    {
      "id": 800,
      "code": "120001",
      "name": "9 payment service Bank",
      "logo": null,
      "cbn_code": "120001"
    },
    {
      "id": 801,
      "code": "090270",
      "name": "AB MICROFINANCE BANK",
      "logo": null,
      "cbn_code": null
    },
    {
      "id": 802,
      "code": "070010",
      "name": "ABBEY MORTGAGE BANK",
      "logo": null,
      "cbn_code": null
    },
    {
      "id": 803,
      "code": "090260",
      "name": "ABOVE ONLY MICROFINANCE BANK",
      "logo": null,
      "cbn_code": null
    },
    {
      "id": 804,
      "code": "090197",
      "name": "ABU MICROFINANCE BANK",
      "logo": null,
      "cbn_code": null
    },
    {
      "id": 805,
      "code": "090424",
      "name": "Abucoop Microfinance Bank",
      "logo": null,
      "cbn_code": null
    },
    {
      "id": 806,
      "code": "000014",
      "name": "ACCESS BANK",
      "logo": null,
      "cbn_code": "044"
    },
    {
      "id": 807,
      "code": "100013",
      "name": "ACCESSMONEY",
      "logo": null,
      "cbn_code": null
    },
    {
      "id": 808,
      "code": "090134",
      "name": "ACCION MFB",
      "logo": null,
      "cbn_code": null
    },
    {
      "id": 809,
      "code": "090483",
      "name": "Ada MFB",
      "logo": null,
      "cbn_code": null
    },
    {
      "id": 810,
      "code": "090160",
      "name": "ADDOSSER MFBB",
      "logo": null,
      "cb
  …
POST

Validate Account

https://viscountmfb.app/public/api/app/corporate-api/v1/validate-account

Name enquiry. Show the returned account_name to your user and make them confirm it before you move money.

Headers

HeaderInNotes
AuthorizationheaderBearer token from Get Access Token.

Body

FieldTypeNotes
bank_codestringDestination bank code. Read it from Bank List rather than hard-coding.
bank_typestringOTHER_BANKS routes over NIP. VISCOUNT settles internally and instantly.
account_numberstringThe account this call acts on.
curl -X POST https://viscountmfb.app/public/api/app/corporate-api/v1/validate-account \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "bank_code": "000015",
  "bank_type": "OTHER_BANKS",
  "account_number": "2009703168"
}'
200 OKapplication/json
{
  "status": "success",
  "message": "Successful",
  "data": {
    "account_name": "ADEDEJI TODIMU AWOLOLA",
    "account_number": "2009703168",
    "bvn": "22000000052",
    "bank_code": "000015",
    "kyc_level": 3,
    "channel_code": 2,
    "session_id": "999999260726100731916237597175"
  }
}
Chapter 05

Bulk Fund Transfer

Queue a batch, validate it, approve it. Built for salaries, vendor runs and loan disbursements.

POST

Create Batch

https://viscountmfb.app/public/api/app/corporate-api/v1/transfer-batch-create

The batch is queued, not sent. It still has to be validated and approved.

Headers

HeaderInNotes
AuthorizationheaderBearer token from Get Access Token.

Body

FieldTypeNotes
from_account_numberstringThe account to debit.
batch_titlestringLabel for the batch, visible in the portal.
longitudestringLongitude of the request origin, for transaction monitoring.
latitudestringLatitude of the request origin, for transaction monitoring.
transfersarrayArray of individual transfer objects making up the batch.
curl -X POST https://viscountmfb.app/public/api/app/corporate-api/v1/transfer-batch-create \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "from_account_number": "5176887565",
  "batch_title": "January 2027 Salary Payment",
  "longitude": "3.35159000",
  "latitude": "6.60547000",
  "transfers": [
    {
      "account_number": "2009703168",
      "narration": "January 2027 Salary Payment",
      "amount": 5000,
      "type": "OTHER_BANKS",
      "bank_code": "000015",
      "bank_name": "Zenith Bank PLC"
    }
  ]
}'
200 OKapplication/json
{
  "status": "success",
  "message": "Bulk Transfer Queued Successfully",
  "data": {
    "batch_id": "051126260726105427845736097354"
  }
}
POST

Validate Batch

https://viscountmfb.app/public/api/app/corporate-api/v1/transfer-batch-validate

Checks every row in the batch before any money moves. Fix errors here, not after approval.

Headers

HeaderInNotes
AuthorizationheaderBearer token from Get Access Token.

Body

FieldTypeNotes
batch_idstringIdentifier returned by Create Batch.
curl -X POST https://viscountmfb.app/public/api/app/corporate-api/v1/transfer-batch-validate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "batch_id": "051126260725101323910652137320"
}'
200 OKapplication/json
{
  "status": "success",
  "message": "Batch Validated Successfully"
}
POST

Approve Batch

Moves money

https://viscountmfb.app/public/api/app/corporate-api/v1/transfer-batch-approve

This is the irreversible one. After approval the transfers are released.

Headers

HeaderInNotes
AuthorizationheaderBearer token from Get Access Token.

Body

FieldTypeNotes
batch_idstringIdentifier returned by Create Batch.
curl -X POST https://viscountmfb.app/public/api/app/corporate-api/v1/transfer-batch-approve \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "batch_id": "051126260725101323910652137320"
}'
200 OKapplication/json
{
  "status": "success",
  "message": "Bulk Transfer Queued Successfully",
  "data": {
    "batch_id": "051126260726105427845736097354"
  }
}
POST

Cancel Batch

https://viscountmfb.app/public/api/app/corporate-api/v1/transfer-batch-cancel

Only works while the batch is still unapproved.

Headers

HeaderInNotes
AuthorizationheaderBearer token from Get Access Token.

Body

FieldTypeNotes
batch_idstringIdentifier returned by Create Batch.
curl -X POST https://viscountmfb.app/public/api/app/corporate-api/v1/transfer-batch-cancel \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "batch_id": "051126260725101323910652137320"
}'
200 OKapplication/json
{
  "status": "success",
  "message": "Bulk Transfer Queued Successfully",
  "data": {
    "batch_id": "051126260726105427845736097354"
  }
}
POST

Get One Batch

https://viscountmfb.app/public/api/app/corporate-api/v1/transfer-batch-get

Headers

HeaderInNotes
AuthorizationheaderBearer token from Get Access Token.

Body

FieldTypeNotes
batch_idstringIdentifier returned by Create Batch.
curl -X POST https://viscountmfb.app/public/api/app/corporate-api/v1/transfer-batch-get \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "batch_id": "051126260725101323910652137320"
}'
200 OKapplication/json
{
  "status": "success",
  "message": "Successful",
  "data": {
    "batch": {
      "batch_title": "January 2027 Salary Payment",
      "batch_id": "051126260725101323910652137320",
      "created_at": "2026-07-25T09:13:23.000000Z",
      "total_transfers": 1,
      "new_transfers": 0,
      "error_transfers": 0,
      "approved_transfers": 1,
      "cancelled_transfers": 0,
      "fromAccount": {
        "account_number": "5176887565",
        "account_type_code": "BUSINESS_STARTER",
        "account_name": "Oxygen",
        "upgrade": false
      },
      "channel": "API",
      "created_by": {
        "name": "API USER",
        "role": "API",
        "user_id": "07714571175724834950"
      }
    },
    "transfers": [
      {
        "account_number": "2009703168",
        "transfer_type": "OTHER_BANKS",
        "amount": "5000",
        "narration": "January 2027 Salary Payment",
        "bank_code": "000015",
        "status": "APPROVED",
        "reference": "051126260725101323703074367502",
        "created_at": "2026-07-25T09:13:23.000000Z",
        "validated": "YES",
        "account_name": "ADEDEJI TODIMU AWOLOLA",
        "message": null
      }
    ]
  }
}
Chapter 06

Direct Debits

Set up a mandate against your customer's account, debit it on your own schedule, then verify the result.

POST

Initiate Direct Debit

https://viscountmfb.app/public/api/app/corporate-api/v1/direct-debit-initiate

Returns an authorization_url. Your customer has to visit it and authorise the mandate before you can debit anything.

Headers

HeaderInNotes
AuthorizationheaderBearer token from Get Access Token.

Body

FieldTypeNotes
account_numberstringThe account this call acts on.
narrationstringText that lands on both statements.
bank_typestringOTHER_BANKS routes over NIP. VISCOUNT settles internally and instantly.
amountnumberAmount in naira.
payer_addressstringAddress of the account holder being debited.
settlement_accountstringViscount account that receives the debited funds.
start_datestringFirst date the mandate may be debited (YYYY-MM-DD).
end_datestringLast date the mandate may be debited (YYYY-MM-DD).
emailstringPayer's email. Used for the mandate authorisation notice.
phone_numberstringPayer's phone number.
bvnstringPayer's BVN.
bank_codestringDestination bank code. Read it from Bank List rather than hard-coding.
curl -X POST https://viscountmfb.app/public/api/app/corporate-api/v1/direct-debit-initiate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "account_number": "2009703168",
  "narration": "OXYGEN LOAN AMOUNT",
  "bank_type": "OTHER_BANKS",
  "amount": 5000,
  "payer_address": "23, OPEBI ROAD IKEJA, LAGOS",
  "settlement_account": "5176887565",
  "start_date": "2026-07-26",
  "end_date": "2026-07-28",
  "email": "adedeji.awolola1@gmail.com",
  "phone_number": "08173716898",
  "bvn": "22000000052",
  "bank_code": "057"
}'
200 OKapplication/json
{
  "status": "success",
  "message": "Successful",
  "data": {
    "authorization_url": "https://authorize.viscountbank.com/01KYF0F4VNCXWHPK9BSY88XJCV",
    "mandate_code": "9282332/20614/4616752689",
    "mandate_reference": "01KYF0F4VNCXWHPK9BSY88XJCV"
  }
}
POST

Get One Direct Debit

https://viscountmfb.app/public/api/app/corporate-api/v1/direct-debit-get

Headers

HeaderInNotes
AuthorizationheaderBearer token from Get Access Token.

Body

FieldTypeNotes
mandate_codestringNIBSS mandate code returned by Initiate Direct Debit.
curl -X POST https://viscountmfb.app/public/api/app/corporate-api/v1/direct-debit-get \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "mandate_code": "4778356/20614/1387248728"
}'
200 OKapplication/json
{
  "status": "success",
  "message": "Successful",
  "data": {
    "business_name": "OXYGEN",
    "start_date": "2026-07-25",
    "end_date": "2026-07-28",
    "amount": "5000.00",
    "bank_code": "057",
    "bank_type": "OTHER_BANKS",
    "mandate_reference": "01KYCAGT45R31YTCC59SCD6Y6Y                                                                                                                                                                              ",
    "account_number": "2009703168",
    "mandate_code": "4778356/20614/1387248728",
    "mandate_status": "PENDING",
    "approved_at": null,
    "cancelled_at": null,
    "banks": [
      {
        "bank_name": "Paystack",
        "account_number": "9880218357",
        "account_name": "NIBSS MANDATE ACTIVATION"
      },
      {
        "bank_name": "Fidelity Bank",
        "account_number": "9020025928",
        "account_name": "NIBSS DIRECT DEBIT"
      }
    ]
  }
}
POST

Debit Mandate

Moves money

https://viscountmfb.app/public/api/app/corporate-api/v1/debit-mandate

Fails with Mandate is not approved until your customer has completed the authorisation flow.

Headers

HeaderInNotes
AuthorizationheaderBearer token from Get Access Token.

Body

FieldTypeNotes
mandate_codestringNIBSS mandate code returned by Initiate Direct Debit.
referencestringYour own unique reference. Use it to reconcile and to keep retries idempotent.
narrationstringText that lands on both statements.
amountnumberAmount in naira.
curl -X POST https://viscountmfb.app/public/api/app/corporate-api/v1/debit-mandate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "mandate_code": "4778356/20614/1387248728",
  "reference": "200970316876545678878",
  "narration": "OXYGEN LOAN AMOUNT",
  "amount": 5000
}'
400 Errorapplication/json
{
  "status": "error",
  "message": "Mandate is not approved."
}
POST

Debit Mandate Verify

https://viscountmfb.app/public/api/app/corporate-api/v1/debit-mandate-verify

Confirm what actually happened to a mandate debit. Poll this with your own reference rather than assuming the debit landed.

Headers

HeaderInNotes
AuthorizationheaderBearer token from Get Access Token.

Body

FieldTypeNotes
referencestringYour own unique reference. Use it to reconcile and to keep retries idempotent.
curl -X POST https://viscountmfb.app/public/api/app/corporate-api/v1/debit-mandate-verify \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "reference": "200970316876545678878"
}'
400 Errorapplication/json
{
  "status": "error",
  "message": "Mandate is not approved."
}