Build on Koodify.
Integrate once against Koodify: embed bank-transfer checkout, verify signed webhooks, split payments across zainboxes, and call stable /api/v1 routes with your merchant keys.
In a nutshell
Use pk_… in the browser for checkout. Use sk_… as X-Api-Key on your server for wallet, transactions, settlements, and billing. Prefer the /api/v1/… paths in these guides.
Quickstart
Collect money with inline checkout (pk_…), or read your zainbox from the server with a merchant secret (sk_…). Never put sk_… in client-side code.
Get your keys
After onboarding you receive pk_live_… (browser) and sk_live_… (server). Keep the secret on your backend only.
Point at the API
API host: https://api.koodify.ng. Checkout script: https://js.koodify.ng/v1/koodify-inline.js.
Checkout and/or server APIs
Browser: koodifyInitPayment with publicKey. Server: X-Api-Key: sk_live_… for wallet, transactions, settlements, and billing.
const zainboxCode = process.env.KOODIFY_ZAINBOX_CODE;
const response = await fetch(
`https://api.koodify.ng/api/v1/zainbox/${zainboxCode}/wallet`,
{
headers: {
"X-Api-Key": process.env.KOODIFY_SECRET_KEY // sk_live_…
}
}
);
const data = await response.json();
// data.data.balances / data.data.virtualAccounts
Authentication
You authenticate with two merchant credentials: a publishable public key for checkout, and a secret key for server APIs.
Environments
Build with test keys (sk_test_… / pk_test_…), then switch to live keys after checkout, webhooks, and (if used) payment splits work end-to-end. Production API host: https://api.koodify.ng.
Test
Use sk_test_… / test data to validate request handling, webhooks, and error paths.
Live
Use sk_live_… and pk_live_… after your integration is verified.
API Guides
Step-by-step integration guides and request samples for the APIs you will call from your app.
Load https://js.koodify.ng/v1/koodify-inline.js and call koodifyInitPayment with amount, txnRef, contact details, callBackUrl, and a publishable public key. Koodify opens a session, shows a one-time virtual account, waits for the transfer, then returns status to your browser callback—and POSTs a signed payment.success event to your callBackUrl even if the tab closed.
<script src="https://js.koodify.ng/v1/koodify-inline.js"></script> <script> koodifyInitPayment({ publicKey: "pk_live_…", amount: "50000", txnRef: "FEES_" + Date.now(), mobileNumber: "08012345678", emailAddress: "[email protected]", callBackUrl: "https://school.example/webhooks/koodify", name: "Ada Lovelace", customParam: { matric: "ND/12/2211", session: "2026" } }, function (response) { console.log(response.status, response.reference, response.customParam); }); </script>
{
"status": "success",
"reference": "FEES_1730544000000",
"description": "Payment was successful",
"payer": {
"email": "[email protected]",
"phone": "08012345678",
"name": "Ada Lovelace"
},
"customParam": {
"matric": "ND/12/2211",
"session": "2026"
}
}
Virtual accounts
Create static virtual accounts with POST /api/v1/va/create and one-time / dynamic accounts with POST /api/v1/dva/create. Inline checkout also creates a temporary account for the payer. Persist the account number and your own customer reference. Optional metadata is stored by Koodify and returned to you—it is not required for the payment rail. Check balances and history with /api/v1/va/… helpers such as GET /api/v1/va/{accountNumber}.
curl -X POST "https://api.koodify.ng/api/v1/va/create" \ -H "Content-Type: application/json" \ -H "X-Api-Key: $KOODIFY_SECRET_KEY" \ -d '{ "bankType": "gtBank", "title": "Ms", "firstName": "Ada", "surname": "Lovelace", "email": "[email protected]", "mobileNumber": "08012345678", "bvn": "12345678901", "dob": "10-12-1815", "gender": "F", "address": "12 Computing Lane, Lagos", "state": "Lagos", "zainboxCode": "main_box", "metadata": { "customerId": "cus_demo_001" } }'
{
"code": "00",
"status": "200 OK",
"description": "successful",
"data": {
"accountNumber": "5006079921",
"accountName": "Ada Lovelace",
"bankName": "gtBank",
"email": "[email protected]",
"metadata": { "customerId": "cus_demo_001" }
}
}
curl -X POST "https://api.koodify.ng/api/v1/dva/create" \ -H "Content-Type: application/json" \ -H "X-Api-Key: $KOODIFY_SECRET_KEY" \ -d '{ "bankType": "gtBank", "email": "[email protected]", "amount": "5000000", "zainboxCode": "main_box", "txnRef": "ORDER_889", "duration": "30", "accountName": "School Fees", "callBackUrl": "https://school.example/webhooks/koodify" }'
{
"code": "00",
"status": "200 OK",
"description": "successful",
"data": {
"accountNumber": "9123456789",
"txnRef": "ORDER_889"
}
}
Payment split
After the customer pays the full amount into a primary zainbox, Koodify can redistribute value to secondary zainboxes. Pick one integration pattern per payment — you do not call both flows for the same checkout. Each share uses either a fixed amount or a percent of the remainder (percents must total 100%). The primary (collector) zainbox must have auto settlement disabled.
Two patterns — choose one
A · Ad-hoc split (server-side): send paymentSplit.shares when you initialize payment — no plan endpoint.
B · Reusable plan (browser checkout): create a plan once, then start each payment with only splitPlanId (inline checkout does not accept ad-hoc shares).
Option A — Ad-hoc split on initialize
No plan call. Your backend starts the payment and includes shares in the same request. Use this when shares change per transaction; browser checkout cannot use this path.
curl -X POST "https://api.koodify.ng/api/v1/checkout/initialize" \ -H "Content-Type: application/json" \ -H "X-Api-Key: $KOODIFY_SECRET_KEY" \ -d '{ "zainboxCode": "main_box", "amount": "1000", "txnRef": "ORDER_889", "mobileNumber": "08012345678", "emailAddress": "[email protected]", "callBackUrl": "https://merchant.example/paid", "paymentSplit": { "shares": [ { "zainboxCode": "tutor_box", "amount": "200" }, { "zainboxCode": "agent_box", "amount": "50" }, { "zainboxCode": "main_box", "percent": 100 } ] } }'
Option B — Reusable plan + checkout
Call the plan API when you create or update the share recipe. For every payment after that, only start checkout with splitPlanId — do not call PUT /api/v1/split-plans again unless the plan changes.
curl -X PUT "https://api.koodify.ng/api/v1/split-plans" \ -H "Content-Type: application/json" \ -H "X-Api-Key: $KOODIFY_SECRET_KEY" \ -d '{ "id": "split_school_fees_v1", "name": "School fees — tutor + agent", "primaryZainboxCode": "main_box", "isActive": true, "shares": [ { "zainboxCode": "tutor_box", "amount": "200" }, { "zainboxCode": "agent_box", "amount": "50" }, { "zainboxCode": "main_box", "percent": 100 } ] }'
koodifyInitPayment({
publicKey: "pk_live_…",
amount: "1000",
txnRef: "ORDER_890",
mobileNumber: "08012345678",
emailAddress: "[email protected]",
callBackUrl: "https://merchant.example/paid",
splitPlanId: "split_school_fees_v1"
}, cb);
After payment succeeds
Either pattern stores a split run for the txnRef. Inspect it anytime:
{
"code": "00",
"data": {
"paymentSplit": {
"txnRef": "ORDER_889",
"primaryZainboxCode": "main_box",
"checkoutAmount": "1000",
"status": "success",
"legs": [
{
"index": 0,
"zainboxCode": "tutor_box",
"role": "transfer",
"amount": "200",
"status": "success"
},
{
"index": 1,
"zainboxCode": "agent_box",
"role": "transfer",
"amount": "50",
"status": "success"
},
{
"index": 2,
"zainboxCode": "main_box",
"role": "keep",
"percent": 100,
"status": "success"
}
]
}
}
}
On payment success, Koodify POSTs a signed JSON event to the callBackUrl you set at checkout—even if the browser tab closed. Verify X-Koodify-Signature (hex HMAC-SHA256 of the raw body) using your webhook signing secret, respond with HTTP 2xx quickly, and process the event asynchronously. Duplicate deliveries are safe to ignore when you key on reference.
POST /webhooks/koodify/ HTTP/1.1 Content-Type: application/json X-Koodify-Event: payment.success X-Koodify-Signature: <hex hmac-sha256 of raw body>
{
"event": "payment.success",
"status": "success",
"reference": "ORDER_889",
"description": "Payment was successful",
"amount": "1000",
"currencyCode": "NGN",
"zainboxCode": "main_box",
"payer": {
"email": "[email protected]",
"phone": "08012345678",
"name": "Ada Lovelace"
},
"customParam": {
"matric": "ND/12/2211",
"session": "2026"
},
"paymentSplit": {
"txnRef": "ORDER_889",
"status": "success",
"legs": []
}
}
// expected = HMAC_SHA256_hex(your_webhook_secret, raw_body) // compare expected to header X-Koodify-Signature (constant-time) // return 200 OK, then mark the order paid using event.reference
API endpoints
Stable /api/v1 routes for integration. Checkout, VAs, splits, and webhooks are covered in the guides above; samples below cover transfers, settlements, and reading your zainbox.
Accept payments
How-to walkthroughs live in Guides. This catalogue lists the stable routes those guides use—without repeating the same samples.
Checkout
Guide · Inline checkout →Browser popup via koodifyInitPayment, or server initialize.
Virtual accounts
Guide · Virtual accounts →Static and dynamic VA create flows, with optional metadata.
Payment splits
Guide · Payment splits →Ad-hoc paymentSplit on initialize, or reusable plans with splitPlanId on checkout.
Transfers
List banks, confirm account names, send funds, and verify the transfer.
List supported banks and codes for transfers and name enquiry.
curl -X GET "https://api.koodify.ng/api/v1/banks" \ -H "X-Api-Key: $KOODIFY_SECRET_KEY"
const response = await fetch("https://api.koodify.ng/api/v1/banks", {
method: "GET",
headers: {
"X-Api-Key": process.env.KOODIFY_SECRET_KEY
}
});
const data = await response.json();
$ch = curl_init("https://api.koodify.ng/api/v1/banks");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Api-Key: " . getenv("KOODIFY_SECRET_KEY"),
],
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
import os, requests
response = requests.get(
"https://api.koodify.ng/api/v1/banks",
headers={"X-Api-Key": os.environ["KOODIFY_SECRET_KEY"]},
timeout=30,
)
data = response.json()
let response = reqwest::Client::new()
.get("https://api.koodify.ng/api/v1/banks")
.header("X-Api-Key", std::env::var("KOODIFY_SECRET_KEY")?)
.send()
.await?;
let data: serde_json::Value = response.json().await?;
{
"code": "00",
"status": "200 OK",
"description": "successful",
"data": [
{
"code": "058",
"name": "Guaranty Trust Bank"
},
{
"code": "50515",
"name": "Moniepoint MFB"
}
]
}
Resolve the account name for a bank code + account number.
curl -X GET "https://api.koodify.ng/api/v1/banks/enquiry?bankCode=058&accountNumber=0123456789" \ -H "X-Api-Key: $KOODIFY_SECRET_KEY"
const response = await fetch("https://api.koodify.ng/api/v1/banks/enquiry?bankCode=058&accountNumber=0123456789", {
method: "GET",
headers: {
"X-Api-Key": process.env.KOODIFY_SECRET_KEY
}
});
const data = await response.json();
$ch = curl_init("https://api.koodify.ng/api/v1/banks/enquiry?bankCode=058&accountNumber=0123456789");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Api-Key: " . getenv("KOODIFY_SECRET_KEY"),
],
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
import os, requests
response = requests.get(
"https://api.koodify.ng/api/v1/banks/enquiry?bankCode=058&accountNumber=0123456789",
headers={"X-Api-Key": os.environ["KOODIFY_SECRET_KEY"]},
timeout=30,
)
data = response.json()
let response = reqwest::Client::new()
.get("https://api.koodify.ng/api/v1/banks/enquiry?bankCode=058&accountNumber=0123456789")
.header("X-Api-Key", std::env::var("KOODIFY_SECRET_KEY")?)
.send()
.await?;
let data: serde_json::Value = response.json().await?;
{
"code": "00",
"status": "200 OK",
"description": "successful",
"data": {
"accountName": "ADA LOVELACE",
"accountNumber": "0123456789",
"bankCode": "058"
}
}
Initiate a single funds transfer (verify separately if needed).
curl -X POST "https://api.koodify.ng/api/v1/transfers" \
-H "Content-Type: application/json" \
-H "X-Api-Key: $KOODIFY_SECRET_KEY" \
-d '{
"destinationAccountNumber": "0123456789",
"destinationBankCode": "058",
"sourceAccountNumber": "5006079921",
"amount": "5000",
"txnRef": "PAYOUT_1001",
"zainboxCode": "main_box",
"narration": "Tutor payout"
}'
const response = await fetch("https://api.koodify.ng/api/v1/transfers", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": process.env.KOODIFY_SECRET_KEY
},
body: JSON.stringify({
"destinationAccountNumber": "0123456789",
"destinationBankCode": "058",
"sourceAccountNumber": "5006079921",
"amount": "5000",
"txnRef": "PAYOUT_1001",
"zainboxCode": "main_box",
"narration": "Tutor payout"
})
});
const data = await response.json();
$ch = curl_init("https://api.koodify.ng/api/v1/transfers");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Api-Key: " . getenv("KOODIFY_SECRET_KEY"),
],
CURLOPT_POSTFIELDS => <<<'JSON'
{
"destinationAccountNumber": "0123456789",
"destinationBankCode": "058",
"sourceAccountNumber": "5006079921",
"amount": "5000",
"txnRef": "PAYOUT_1001",
"zainboxCode": "main_box",
"narration": "Tutor payout"
}
JSON,
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
import os, requests
response = requests.post(
"https://api.koodify.ng/api/v1/transfers",
headers={
"X-Api-Key": os.environ["KOODIFY_SECRET_KEY"],
"Content-Type": "application/json",
},
json={
"destinationAccountNumber": "0123456789",
"destinationBankCode": "058",
"sourceAccountNumber": "5006079921",
"amount": "5000",
"txnRef": "PAYOUT_1001",
"zainboxCode": "main_box",
"narration": "Tutor payout"
},
timeout=30,
)
data = response.json()
let body = serde_json::from_str(r#"{
"destinationAccountNumber": "0123456789",
"destinationBankCode": "058",
"sourceAccountNumber": "5006079921",
"amount": "5000",
"txnRef": "PAYOUT_1001",
"zainboxCode": "main_box",
"narration": "Tutor payout"
}"#)?;
let response = reqwest::Client::new()
.post("https://api.koodify.ng/api/v1/transfers")
.header("X-Api-Key", std::env::var("KOODIFY_SECRET_KEY")?)
.json(&body)
.send()
.await?;
let data: serde_json::Value = response.json().await?;
{
"code": "00",
"status": "200 OK",
"description": "successful",
"data": {
"txnRef": "PAYOUT_1001",
"status": "pending"
}
}
Verify a transfer by its txnRef.
curl -X GET "https://api.koodify.ng/api/v1/transfers/ORDER_889/verify" \ -H "X-Api-Key: $KOODIFY_SECRET_KEY"
const response = await fetch("https://api.koodify.ng/api/v1/transfers/ORDER_889/verify", {
method: "GET",
headers: {
"X-Api-Key": process.env.KOODIFY_SECRET_KEY
}
});
const data = await response.json();
$ch = curl_init("https://api.koodify.ng/api/v1/transfers/ORDER_889/verify");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Api-Key: " . getenv("KOODIFY_SECRET_KEY"),
],
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
import os, requests
response = requests.get(
"https://api.koodify.ng/api/v1/transfers/ORDER_889/verify",
headers={"X-Api-Key": os.environ["KOODIFY_SECRET_KEY"]},
timeout=30,
)
data = response.json()
let response = reqwest::Client::new()
.get("https://api.koodify.ng/api/v1/transfers/ORDER_889/verify")
.header("X-Api-Key", std::env::var("KOODIFY_SECRET_KEY")?)
.send()
.await?;
let data: serde_json::Value = response.json().await?;
{
"code": "00",
"status": "200 OK",
"description": "successful",
"data": {
"txnRef": "PAYOUT_1001",
"status": "success",
"amount": "5000"
}
}
Settlements
Configure settlement behaviour and inspect settlement accounts for a zainbox.
Create or update settlement configuration for a zainbox. Use GET /api/v1/settlements to read current settings.
curl -X POST "https://api.koodify.ng/api/v1/settlements" \
-H "Content-Type: application/json" \
-H "X-Api-Key: $KOODIFY_SECRET_KEY" \
-d '{
"name": "Main settlement",
"scheduleType": "T1",
"zainboxCode": "main_box",
"status": false,
"settlementAccountList": [
{
"accountNumber": "0123456789",
"bankCode": "058",
"accountName": "SCHOOL LTD"
}
]
}'
const response = await fetch("https://api.koodify.ng/api/v1/settlements", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": process.env.KOODIFY_SECRET_KEY
},
body: JSON.stringify({
"name": "Main settlement",
"scheduleType": "T1",
"zainboxCode": "main_box",
"status": false,
"settlementAccountList": [
{
"accountNumber": "0123456789",
"bankCode": "058",
"accountName": "SCHOOL LTD"
}
]
})
});
const data = await response.json();
$ch = curl_init("https://api.koodify.ng/api/v1/settlements");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Api-Key: " . getenv("KOODIFY_SECRET_KEY"),
],
CURLOPT_POSTFIELDS => <<<'JSON'
{
"name": "Main settlement",
"scheduleType": "T1",
"zainboxCode": "main_box",
"status": false,
"settlementAccountList": [
{
"accountNumber": "0123456789",
"bankCode": "058",
"accountName": "SCHOOL LTD"
}
]
}
JSON,
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
import os, requests
response = requests.post(
"https://api.koodify.ng/api/v1/settlements",
headers={
"X-Api-Key": os.environ["KOODIFY_SECRET_KEY"],
"Content-Type": "application/json",
},
json={
"name": "Main settlement",
"scheduleType": "T1",
"zainboxCode": "main_box",
"status": false,
"settlementAccountList": [
{
"accountNumber": "0123456789",
"bankCode": "058",
"accountName": "SCHOOL LTD"
}
]
},
timeout=30,
)
data = response.json()
let body = serde_json::from_str(r#"{
"name": "Main settlement",
"scheduleType": "T1",
"zainboxCode": "main_box",
"status": false,
"settlementAccountList": [
{
"accountNumber": "0123456789",
"bankCode": "058",
"accountName": "SCHOOL LTD"
}
]
}"#)?;
let response = reqwest::Client::new()
.post("https://api.koodify.ng/api/v1/settlements")
.header("X-Api-Key", std::env::var("KOODIFY_SECRET_KEY")?)
.json(&body)
.send()
.await?;
let data: serde_json::Value = response.json().await?;
{
"code": "00",
"status": "200 OK",
"description": "successful",
"data": {
"zainboxCode": "main_box",
"status": false
}
}
Read settlement configuration (pass zainbox query as required by your integration).
curl -X GET "https://api.koodify.ng/api/v1/settlements?zainboxCode=main_box" \ -H "X-Api-Key: $KOODIFY_SECRET_KEY"
const response = await fetch("https://api.koodify.ng/api/v1/settlements?zainboxCode=main_box", {
method: "GET",
headers: {
"X-Api-Key": process.env.KOODIFY_SECRET_KEY
}
});
const data = await response.json();
$ch = curl_init("https://api.koodify.ng/api/v1/settlements?zainboxCode=main_box");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Api-Key: " . getenv("KOODIFY_SECRET_KEY"),
],
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
import os, requests
response = requests.get(
"https://api.koodify.ng/api/v1/settlements?zainboxCode=main_box",
headers={"X-Api-Key": os.environ["KOODIFY_SECRET_KEY"]},
timeout=30,
)
data = response.json()
let response = reqwest::Client::new()
.get("https://api.koodify.ng/api/v1/settlements?zainboxCode=main_box")
.header("X-Api-Key", std::env::var("KOODIFY_SECRET_KEY")?)
.send()
.await?;
let data: serde_json::Value = response.json().await?;
{
"code": "00",
"status": "200 OK",
"description": "successful",
"data": {
"zainboxCode": "main_box",
"status": false,
"scheduleType": "T1"
}
}
Settlement history for a zainbox.
curl -X GET "https://api.koodify.ng/api/v1/settlements/history/main_box" \ -H "X-Api-Key: $KOODIFY_SECRET_KEY"
const response = await fetch("https://api.koodify.ng/api/v1/settlements/history/main_box", {
method: "GET",
headers: {
"X-Api-Key": process.env.KOODIFY_SECRET_KEY
}
});
const data = await response.json();
$ch = curl_init("https://api.koodify.ng/api/v1/settlements/history/main_box");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Api-Key: " . getenv("KOODIFY_SECRET_KEY"),
],
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
import os, requests
response = requests.get(
"https://api.koodify.ng/api/v1/settlements/history/main_box",
headers={"X-Api-Key": os.environ["KOODIFY_SECRET_KEY"]},
timeout=30,
)
data = response.json()
let response = reqwest::Client::new()
.get("https://api.koodify.ng/api/v1/settlements/history/main_box")
.header("X-Api-Key", std::env::var("KOODIFY_SECRET_KEY")?)
.send()
.await?;
let data: serde_json::Value = response.json().await?;
{
"code": "00",
"status": "200 OK",
"description": "successful",
"data": [
{
"amount": "50000",
"status": "success",
"settledAt": "2026-11-01T10:00:00Z"
}
]
}
Fetch the settlement account for a zainbox.
curl -X GET "https://api.koodify.ng/api/v1/settlements/internal/main_box" \ -H "X-Api-Key: $KOODIFY_SECRET_KEY"
const response = await fetch("https://api.koodify.ng/api/v1/settlements/internal/main_box", {
method: "GET",
headers: {
"X-Api-Key": process.env.KOODIFY_SECRET_KEY
}
});
const data = await response.json();
$ch = curl_init("https://api.koodify.ng/api/v1/settlements/internal/main_box");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Api-Key: " . getenv("KOODIFY_SECRET_KEY"),
],
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
import os, requests
response = requests.get(
"https://api.koodify.ng/api/v1/settlements/internal/main_box",
headers={"X-Api-Key": os.environ["KOODIFY_SECRET_KEY"]},
timeout=30,
)
data = response.json()
let response = reqwest::Client::new()
.get("https://api.koodify.ng/api/v1/settlements/internal/main_box")
.header("X-Api-Key", std::env::var("KOODIFY_SECRET_KEY")?)
.send()
.await?;
let data: serde_json::Value = response.json().await?;
{
"code": "00",
"status": "200 OK",
"description": "successful",
"data": {
"accountNumber": "1100001234",
"bankName": "Access Bank"
}
}
Transactions
Confirm payments, look up activity, and read your zainbox wallet and transactions.
Confirmation
API Guide · Webhooks →Prefer the signed payment.success webhook. Use deposit verify when you need a server-side status check.
Koodify activity events for one payment reference (initialize, VA create, status, webhook).
curl -X GET "https://api.koodify.ng/api/v1/activity/ORDER_889" \ -H "X-Api-Key: $KOODIFY_SECRET_KEY"
const response = await fetch("https://api.koodify.ng/api/v1/activity/ORDER_889", {
method: "GET",
headers: {
"X-Api-Key": process.env.KOODIFY_SECRET_KEY
}
});
const data = await response.json();
$ch = curl_init("https://api.koodify.ng/api/v1/activity/ORDER_889");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Api-Key: " . getenv("KOODIFY_SECRET_KEY"),
],
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
import os, requests
response = requests.get(
"https://api.koodify.ng/api/v1/activity/ORDER_889",
headers={"X-Api-Key": os.environ["KOODIFY_SECRET_KEY"]},
timeout=30,
)
data = response.json()
let response = reqwest::Client::new()
.get("https://api.koodify.ng/api/v1/activity/ORDER_889")
.header("X-Api-Key", std::env::var("KOODIFY_SECRET_KEY")?)
.send()
.await?;
let data: serde_json::Value = response.json().await?;
{
"code": "00",
"status": "200 OK",
"description": "successful",
"data": [
{
"type": "checkout.initialize",
"txnRef": "ORDER_889",
"at": "2026-11-02T10:00:00Z"
},
{
"type": "payment.success",
"txnRef": "ORDER_889",
"at": "2026-11-02T10:04:12Z"
}
]
}
Recent activity for your account.
curl -X GET "https://api.koodify.ng/api/v1/activity" \ -H "X-Api-Key: $KOODIFY_SECRET_KEY"
const response = await fetch("https://api.koodify.ng/api/v1/activity", {
method: "GET",
headers: {
"X-Api-Key": process.env.KOODIFY_SECRET_KEY
}
});
const data = await response.json();
$ch = curl_init("https://api.koodify.ng/api/v1/activity");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Api-Key: " . getenv("KOODIFY_SECRET_KEY"),
],
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
import os, requests
response = requests.get(
"https://api.koodify.ng/api/v1/activity",
headers={"X-Api-Key": os.environ["KOODIFY_SECRET_KEY"]},
timeout=30,
)
data = response.json()
let response = reqwest::Client::new()
.get("https://api.koodify.ng/api/v1/activity")
.header("X-Api-Key", std::env::var("KOODIFY_SECRET_KEY")?)
.send()
.await?;
let data: serde_json::Value = response.json().await?;
{
"code": "00",
"status": "200 OK",
"description": "successful",
"data": [
{
"type": "payment.success",
"txnRef": "ORDER_889",
"at": "2026-11-02T10:04:12Z"
}
]
}
Transactions summary + list for your account. Also available as /api/v1/transactions/summary and /list.
curl -X GET "https://api.koodify.ng/api/v1/transactions" \ -H "X-Api-Key: $KOODIFY_SECRET_KEY"
const response = await fetch("https://api.koodify.ng/api/v1/transactions", {
method: "GET",
headers: {
"X-Api-Key": process.env.KOODIFY_SECRET_KEY
}
});
const data = await response.json();
$ch = curl_init("https://api.koodify.ng/api/v1/transactions");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Api-Key: " . getenv("KOODIFY_SECRET_KEY"),
],
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
import os, requests
response = requests.get(
"https://api.koodify.ng/api/v1/transactions",
headers={"X-Api-Key": os.environ["KOODIFY_SECRET_KEY"]},
timeout=30,
)
data = response.json()
let response = reqwest::Client::new()
.get("https://api.koodify.ng/api/v1/transactions")
.header("X-Api-Key", std::env::var("KOODIFY_SECRET_KEY")?)
.send()
.await?;
let data: serde_json::Value = response.json().await?;
{
"code": "00",
"status": "200 OK",
"description": "successful",
"data": {
"summary": {
"count": 12,
"volume": "450000"
},
"transactions": [
{
"txnRef": "ORDER_889",
"amount": "1000",
"status": "success"
}
]
}
}
Zainbox profile plus balances.
curl -X GET "https://api.koodify.ng/api/v1/zainbox/main_box" \ -H "X-Api-Key: $KOODIFY_SECRET_KEY"
const response = await fetch("https://api.koodify.ng/api/v1/zainbox/main_box", {
method: "GET",
headers: {
"X-Api-Key": process.env.KOODIFY_SECRET_KEY
}
});
const data = await response.json();
$ch = curl_init("https://api.koodify.ng/api/v1/zainbox/main_box");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Api-Key: " . getenv("KOODIFY_SECRET_KEY"),
],
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
import os, requests
response = requests.get(
"https://api.koodify.ng/api/v1/zainbox/main_box",
headers={"X-Api-Key": os.environ["KOODIFY_SECRET_KEY"]},
timeout=30,
)
data = response.json()
let response = reqwest::Client::new()
.get("https://api.koodify.ng/api/v1/zainbox/main_box")
.header("X-Api-Key", std::env::var("KOODIFY_SECRET_KEY")?)
.send()
.await?;
let data: serde_json::Value = response.json().await?;
{
"code": "00",
"status": "200 OK",
"description": "successful",
"data": {
"profile": {
"codeName": "main_box",
"name": "Main box"
},
"balances": [
{
"accountNumber": "5006079921",
"balance": "125000.00"
}
]
}
}
List zainboxes for the authenticated merchant.
curl -X GET "https://api.koodify.ng/api/v1/zainboxes" \ -H "X-Api-Key: $KOODIFY_SECRET_KEY"
const response = await fetch("https://api.koodify.ng/api/v1/zainboxes", {
method: "GET",
headers: {
"X-Api-Key": process.env.KOODIFY_SECRET_KEY
}
});
const data = await response.json();
$ch = curl_init("https://api.koodify.ng/api/v1/zainboxes");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Api-Key: " . getenv("KOODIFY_SECRET_KEY"),
],
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
import os, requests
response = requests.get(
"https://api.koodify.ng/api/v1/zainboxes",
headers={"X-Api-Key": os.environ["KOODIFY_SECRET_KEY"]},
timeout=30,
)
data = response.json()
let response = reqwest::Client::new()
.get("https://api.koodify.ng/api/v1/zainboxes")
.header("X-Api-Key", std::env::var("KOODIFY_SECRET_KEY")?)
.send()
.await?;
let data: serde_json::Value = response.json().await?;
{
"code": "00",
"status": "200 OK",
"description": "successful",
"data": [
{
"codeName": "main_box",
"name": "Main box"
}
]
}
Virtual accounts and balances for a zainbox—the usual read after checkout goes live.
curl -X GET "https://api.koodify.ng/api/v1/zainbox/main_box/wallet" \ -H "X-Api-Key: $KOODIFY_SECRET_KEY"
const response = await fetch("https://api.koodify.ng/api/v1/zainbox/main_box/wallet", {
method: "GET",
headers: {
"X-Api-Key": process.env.KOODIFY_SECRET_KEY
}
});
const data = await response.json();
$ch = curl_init("https://api.koodify.ng/api/v1/zainbox/main_box/wallet");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Api-Key: " . getenv("KOODIFY_SECRET_KEY"),
],
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
import os, requests
response = requests.get(
"https://api.koodify.ng/api/v1/zainbox/main_box/wallet",
headers={"X-Api-Key": os.environ["KOODIFY_SECRET_KEY"]},
timeout=30,
)
data = response.json()
let response = reqwest::Client::new()
.get("https://api.koodify.ng/api/v1/zainbox/main_box/wallet")
.header("X-Api-Key", std::env::var("KOODIFY_SECRET_KEY")?)
.send()
.await?;
let data: serde_json::Value = response.json().await?;
{
"code": "00",
"status": "200 OK",
"description": "successful",
"data": {
"virtualAccounts": [
{
"accountNumber": "5006079921"
}
],
"balances": [
{
"accountNumber": "5006079921",
"balance": "125000.00"
}
]
}
}
Error handling
API responses use a standard envelope: code, status, description, and data. Treat a payment as successful only when the payment status is explicitly successful (for example success or paid). Reused txnRef values fail checkout—issue a fresh reference for each attempt. On webhooks, verify the signature, acknowledge with 2xx, and ignore duplicates for the same reference.
Security best practices
Keep sk_… on the server only; publish only pk_… in the browser. Verify X-Koodify-Signature on the raw webhook body. Never log API keys, BVN, or other sensitive customer data. Use an HTTPS callBackUrl that accepts POST.
Ready to build something reliable?
Call your zainbox with sk_…, embed inline checkout with pk_…, and verify webhooks before going live.