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
referencestringZevPay 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)
statusstringFilter by status: pending, completed, failed
typestringFilter by type: bank_transfer, payid
searchstringSearch 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