Skip to content

Transfer API ​

Programmatically send money from a wallet to bank accounts or other ZevPay users via PayID.

Security Notice

Transfer endpoints move funds out of your wallet. These endpoints require a secret key and a wallet with programmable debit enabled. Keep your secret keys secure and consider IP whitelisting.

Authentication ​

All transfer endpoints require:

  1. A secret key (sk_live_* or sk_test_*) passed via Authorization: Bearer sk_live_...
  2. The API key must have the transfers permission
  3. A programmable-debit wallet must be linked to the API key

See Authentication for details.


List Banks ​

Returns all supported banks for external transfers.

GET /v1/checkout/transfer/banks

Authentication ​

Secret key required. No transfers permission needed.

Example Request ​

bash
curl https://api.zevpaycheckout.com/v1/checkout/transfer/banks \
  -H "Authorization: Bearer sk_live_your_secret_key"
javascript
const response = await fetch('https://api.zevpaycheckout.com/v1/checkout/transfer/banks', {
  headers: {
    'Authorization': 'Bearer sk_live_your_secret_key',
  },
});
const { data } = await response.json();
console.log(data.banks);
python
import requests

response = requests.get(
    'https://api.zevpaycheckout.com/v1/checkout/transfer/banks',
    headers={'Authorization': 'Bearer sk_live_your_secret_key'},
)
banks = response.json()['data']['banks']

Response ​

json
{
  "success": true,
  "data": {
    "banks": [
      {
        "bankCode": "044",
        "bankName": "Access Bank"
      },
      {
        "bankCode": "058",
        "bankName": "Guaranty Trust Bank"
      }
    ]
  }
}

Resolve Bank Account ​

Verify a bank account number and retrieve the account holder's name (name enquiry).

POST /v1/checkout/transfer/banks/resolve

Authentication ​

Secret key required. No transfers permission needed.

Request Body ​

ParameterTypeRequiredDescription
account_numberstringYes10-digit bank account number
bank_codestringYesBank code from the List Banks endpoint

Example Request ​

bash
curl -X POST https://api.zevpaycheckout.com/v1/checkout/transfer/banks/resolve \
  -H "Authorization: Bearer sk_live_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "account_number": "0123456789",
    "bank_code": "058"
  }'
javascript
const response = await fetch('https://api.zevpaycheckout.com/v1/checkout/transfer/banks/resolve', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_live_your_secret_key',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    account_number: '0123456789',
    bank_code: '058',
  }),
});
const { data } = await response.json();
console.log(data.account_name); // "JOHN DOE"
python
import requests

response = requests.post(
    'https://api.zevpaycheckout.com/v1/checkout/transfer/banks/resolve',
    headers={'Authorization': 'Bearer sk_live_your_secret_key'},
    json={
        'account_number': '0123456789',
        'bank_code': '058',
    },
)
data = response.json()['data']
print(data['account_name'])  # "JOHN DOE"

Response ​

json
{
  "success": true,
  "data": {
    "account_name": "JOHN DOE",
    "account_number": "0123456789",
    "bank_code": "058"
  }
}

Calculate Charges ​

Calculate the fees for a bank transfer before initiating it.

POST /v1/checkout/transfer/charges

Authentication ​

Secret key required. transfers permission required.

Request Body ​

ParameterTypeRequiredDescription
amountintegerYesTransfer amount in kobo
bank_codestringYesDestination bank code

Example Request ​

bash
curl -X POST https://api.zevpaycheckout.com/v1/checkout/transfer/charges \
  -H "Authorization: Bearer sk_live_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 500000,
    "bank_code": "058"
  }'
javascript
const response = await fetch('https://api.zevpaycheckout.com/v1/checkout/transfer/charges', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_live_your_secret_key',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    amount: 500000,
    bank_code: '058',
  }),
});
const { data } = await response.json();
console.log(data);

Response ​

json
{
  "success": true,
  "data": {
    "amount": 500000,
    "fee": 2625,
    "vat": 197,
    "stamp_duty": 0,
    "total": 502822,
    "currency": "NGN"
  }
}

Response Fields ​

FieldTypeDescription
amountintegerOriginal transfer amount in kobo
feeintegerTransfer fee in kobo
vatintegerVAT on the fee in kobo
stamp_dutyintegerStamp duty in kobo (applied on amounts ≥ ₦10,000)
totalintegerTotal debit amount (amount + fee + vat + stamp_duty) in kobo
currencystringCurrency code ("NGN")

Get Balance ​

Retrieve the available balance of the linked wallet.

GET /v1/checkout/transfer/balance

Authentication ​

Secret key required. transfers permission required.

Example Request ​

bash
curl https://api.zevpaycheckout.com/v1/checkout/transfer/balance \
  -H "Authorization: Bearer sk_live_your_secret_key"
javascript
const response = await fetch('https://api.zevpaycheckout.com/v1/checkout/transfer/balance', {
  headers: {
    'Authorization': 'Bearer sk_live_your_secret_key',
  },
});
const { data } = await response.json();
console.log(`Balance: ₦${(data.available_balance / 100).toFixed(2)}`);

Response ​

json
{
  "success": true,
  "data": {
    "available_balance": 15000000,
    "currency": "NGN"
  }
}
FieldTypeDescription
available_balanceintegerAvailable balance in kobo
currencystringCurrency code ("NGN")

Initiate Transfer ​

Send money to a bank account or ZevPay user. The type field determines the transfer destination.

POST /v1/checkout/transfer

Authentication ​

Secret key required. transfers permission required.

Request Body: Bank Transfer ​

ParameterTypeRequiredDescription
typestringYesMust be "bank_transfer"
account_numberstringYes10-digit destination account number
bank_codestringYesDestination bank code
account_namestringYesAccount holder name (from Resolve)
amountintegerYesAmount in kobo (min: 10,000 = ₦100, max: 500,000,000 = ₦5,000,000)
narrationstringNoTransfer description (max 100 characters)
referencestringNoYour unique reference for idempotency (max 128 characters)
metadataobjectNoCustom key-value metadata

Request Body: PayID Transfer ​

ParameterTypeRequiredDescription
typestringYesMust be "payid"
pay_idstringYesRecipient's ZevPay ID (e.g., "john" or "@john")
amountintegerYesAmount in kobo (min: 10,000, max: 500,000,000)
narrationstringNoTransfer description (max 100 characters)
referencestringNoYour unique reference for idempotency (max 128 characters)
metadataobjectNoCustom key-value metadata

Example Request: Bank Transfer ​

bash
curl -X POST https://api.zevpaycheckout.com/v1/checkout/transfer \
  -H "Authorization: Bearer sk_live_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "bank_transfer",
    "account_number": "0123456789",
    "bank_code": "058",
    "account_name": "JOHN DOE",
    "amount": 500000,
    "narration": "Vendor payment",
    "reference": "payout_001",
    "metadata": {
      "invoice_id": "INV-2026-001"
    }
  }'
javascript
const response = await fetch('https://api.zevpaycheckout.com/v1/checkout/transfer', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_live_your_secret_key',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    type: 'bank_transfer',
    account_number: '0123456789',
    bank_code: '058',
    account_name: 'JOHN DOE',
    amount: 500000,
    narration: 'Vendor payment',
    reference: 'payout_001',
    metadata: { invoice_id: 'INV-2026-001' },
  }),
});
const { data } = await response.json();
console.log(data.reference, data.status);
python
import requests

response = requests.post(
    'https://api.zevpaycheckout.com/v1/checkout/transfer',
    headers={'Authorization': 'Bearer sk_live_your_secret_key'},
    json={
        'type': 'bank_transfer',
        'account_number': '0123456789',
        'bank_code': '058',
        'account_name': 'JOHN DOE',
        'amount': 500000,
        'narration': 'Vendor payment',
        'reference': 'payout_001',
        'metadata': {'invoice_id': 'INV-2026-001'},
    },
)
data = response.json()['data']
print(data['reference'], data['status'])

Example Request: PayID Transfer ​

bash
curl -X POST https://api.zevpaycheckout.com/v1/checkout/transfer \
  -H "Authorization: Bearer sk_live_your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "payid",
    "pay_id": "john",
    "amount": 250000,
    "narration": "Freelance payment"
  }'
javascript
const response = await fetch('https://api.zevpaycheckout.com/v1/checkout/transfer', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer sk_live_your_secret_key',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    type: 'payid',
    pay_id: 'john',
    amount: 250000,
    narration: 'Freelance payment',
  }),
});
const { data } = await response.json();
console.log(data.reference, data.status);

Response ​

json
{
  "success": true,
  "data": {
    "reference": "ZVP-TRF-abc123def456",
    "public_id": "xK9mQ2pL4vR7nW3jY",
    "type": "bank_transfer",
    "status": "completed",
    "amount": 500000,
    "fees": 2625,
    "vat": 197,
    "stamp_duty": 0,
    "currency": "NGN",
    "narration": "Vendor payment",
    "recipient": {
      "name": "JOHN DOE",
      "bank": "Guaranty Trust Bank",
      "account_number": "0123456789"
    },
    "merchant_reference": "payout_001",
    "metadata": {
      "invoice_id": "INV-2026-001"
    },
    "created_at": "2026-03-08T14:30:00.000Z",
    "completed_at": "2026-03-08T14:30:02.000Z"
  }
}

Response Fields ​

FieldTypeDescription
referencestringOur transaction reference
public_idstringPublic transaction identifier
typestring"bank_transfer" or "payid"
statusstring"pending", "completed", or "failed"
amountintegerTransfer amount in kobo
feesintegerTransfer fees in kobo (0 for PayID transfers)
vatintegerVAT in kobo
stamp_dutyintegerStamp duty in kobo
currencystringCurrency code ("NGN")
narrationstring | nullTransfer description
recipient.namestringRecipient name
recipient.bankstring | nullRecipient bank (bank transfers only)
recipient.account_numberstring | nullRecipient account number (bank transfers only)
merchant_referencestring | nullYour idempotency reference
metadataobject | nullYour custom metadata
created_atstringISO 8601 timestamp
completed_atstring | nullISO 8601 timestamp (when completed)

Transfer Status Lifecycle ​

StatusDescription
pendingTransfer is being processed (bank transfers only)
completedTransfer delivered successfully
failedTransfer was rejected by the bank

INFO

PayID transfers complete instantly. The status will always be "completed" on success.

Bank transfers may briefly show "pending" before resolving to "completed" or "failed".

Idempotency ​

If you provide a reference and a transfer with that reference already exists, the API returns the existing transfer instead of creating a duplicate. The response will be identical to the original.


List Transfers ​

Retrieve a paginated list of transfers from the linked wallet.

GET /v1/checkout/transfer

Authentication ​

Secret key required. transfers permission required.

Query Parameters ​

ParameterTypeDefaultDescription
pageinteger1Page number
page_sizeinteger20Items per page (max 100)
statusstringnoneFilter by status: pending, completed, failed
typestringnoneFilter by type: bank_transfer, payid
searchstringnoneSearch by reference, recipient name, or account number

Example Request ​

bash
curl "https://api.zevpaycheckout.com/v1/checkout/transfer?page=1&page_size=10&status=completed" \
  -H "Authorization: Bearer sk_live_your_secret_key"
javascript
const params = new URLSearchParams({
  page: '1',
  page_size: '10',
  status: 'completed',
});

const response = await fetch(
  `https://api.zevpaycheckout.com/v1/checkout/transfer?${params}`,
  {
    headers: {
      'Authorization': 'Bearer sk_live_your_secret_key',
    },
  },
);
const { data } = await response.json();
console.log(`${data.total} transfers, page ${data.page}/${data.total_pages}`);

Response ​

json
{
  "success": true,
  "data": {
    "items": [
      {
        "reference": "ZVP-TRF-abc123def456",
        "public_id": "xK9mQ2pL4vR7nW3jY",
        "type": "bank_transfer",
        "status": "completed",
        "amount": 500000,
        "fees": 2625,
        "vat": 197,
        "stamp_duty": 0,
        "currency": "NGN",
        "narration": "Vendor payment",
        "recipient": {
          "name": "JOHN DOE",
          "bank": "Guaranty Trust Bank",
          "account_number": "0123456789"
        },
        "merchant_reference": "payout_001",
        "metadata": null,
        "created_at": "2026-03-08T14:30:00.000Z",
        "completed_at": "2026-03-08T14:30:02.000Z"
      }
    ],
    "total": 42,
    "page": 1,
    "page_size": 10,
    "total_pages": 5
  }
}

Get Transfer ​

Retrieve a single transfer by its reference.

GET /v1/checkout/transfer/:reference

Authentication ​

Secret key required. transfers permission required.

Path Parameters ​

ParameterTypeDescription
referencestringThe transfer reference

Example Request ​

bash
curl https://api.zevpaycheckout.com/v1/checkout/transfer/ZVP-TRF-abc123def456 \
  -H "Authorization: Bearer sk_live_your_secret_key"
javascript
const reference = 'ZVP-TRF-abc123def456';
const response = await fetch(
  `https://api.zevpaycheckout.com/v1/checkout/transfer/${reference}`,
  {
    headers: {
      'Authorization': 'Bearer sk_live_your_secret_key',
    },
  },
);
const { data } = await response.json();
console.log(data.status);

Response ​

Returns the same transfer object as the initiate endpoint.


Verify Transfer ​

Check the current status of a transfer. For bank transfers, this queries the banking provider for the latest status.

GET /v1/checkout/transfer/:reference/verify

Authentication ​

Secret key required. transfers permission required.

Path Parameters ​

ParameterTypeDescription
referencestringThe transfer reference

Example Request ​

bash
curl https://api.zevpaycheckout.com/v1/checkout/transfer/ZVP-TRF-abc123def456/verify \
  -H "Authorization: Bearer sk_live_your_secret_key"
javascript
const reference = 'ZVP-TRF-abc123def456';
const response = await fetch(
  `https://api.zevpaycheckout.com/v1/checkout/transfer/${reference}/verify`,
  {
    headers: {
      'Authorization': 'Bearer sk_live_your_secret_key',
    },
  },
);
const { data } = await response.json();
console.log(data.status); // "completed" | "pending" | "failed"

Response ​

Returns the same transfer object as the initiate endpoint, with the most up-to-date status.

TIP

Use the verify endpoint after receiving a transfer.failed webhook to confirm the final status, or to poll pending transfers that haven't received a webhook yet.


Errors ​

StatusCodeDescription
400Bad RequestInvalid parameters (missing fields, invalid amount range)
400Bad RequestTransfer not found (wrong reference)
400Bad RequestInsufficient balance
400Bad RequestSelf-transfer not allowed (PayID transfers)
403ForbiddenSecret key required
403ForbiddenAPI key missing transfers permission
403ForbiddenNo transfer wallet linked to API key
403ForbiddenProgrammable debit disabled on wallet
403ForbiddenWallet does not belong to this business

Try it: List Banks ​

API Playground
GEThttps://api.zevpaycheckout.com/v1/checkout/transfer/banks

Try it: Resolve Bank Account ​

API Playground
POSThttps://api.zevpaycheckout.com/v1/checkout/transfer/banks/resolve

Try it: Get Balance ​

API Playground
GEThttps://api.zevpaycheckout.com/v1/checkout/transfer/balance

Try it: Initiate Transfer ​

API Playground
POSThttps://api.zevpaycheckout.com/v1/checkout/transfer

Try it: List Transfers ​

API Playground
GEThttps://api.zevpaycheckout.com/v1/checkout/transfer

ZevPay Checkout Developer Documentation