curl --request POST \
--url https://sandbox.users.niobi.co/api/v4/niobi-unified-collections \
--header 'Content-Type: application/json' \
--data '
{
"client_id": "<string>",
"sender": "<string>",
"timestamp": 123,
"salt": "<string>",
"signature": "<string>",
"params": {
"amount": 123,
"mobile": "<string>",
"country_id": 123,
"currency": "<string>",
"payment_method_type": "<string>",
"callback_url": "<string>",
"third_party_reference_1": "<string>",
"third_party_reference_2": "<string>",
"sub_merchant_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"first_name": "<string>",
"last_name": "<string>",
"account_name": "<string>",
"bank_code": "<string>",
"successful_url": "<string>",
"failure_url": "<string>",
"id_number": "<string>"
}
}
'import requests
url = "https://sandbox.users.niobi.co/api/v4/niobi-unified-collections"
payload = {
"client_id": "<string>",
"sender": "<string>",
"timestamp": 123,
"salt": "<string>",
"signature": "<string>",
"params": {
"amount": 123,
"mobile": "<string>",
"country_id": 123,
"currency": "<string>",
"payment_method_type": "<string>",
"callback_url": "<string>",
"third_party_reference_1": "<string>",
"third_party_reference_2": "<string>",
"sub_merchant_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"first_name": "<string>",
"last_name": "<string>",
"account_name": "<string>",
"bank_code": "<string>",
"successful_url": "<string>",
"failure_url": "<string>",
"id_number": "<string>"
}
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
client_id: '<string>',
sender: '<string>',
timestamp: 123,
salt: '<string>',
signature: '<string>',
params: {
amount: 123,
mobile: '<string>',
country_id: 123,
currency: '<string>',
payment_method_type: '<string>',
callback_url: '<string>',
third_party_reference_1: '<string>',
third_party_reference_2: '<string>',
sub_merchant_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
first_name: '<string>',
last_name: '<string>',
account_name: '<string>',
bank_code: '<string>',
successful_url: '<string>',
failure_url: '<string>',
id_number: '<string>'
}
})
};
fetch('https://sandbox.users.niobi.co/api/v4/niobi-unified-collections', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://sandbox.users.niobi.co/api/v4/niobi-unified-collections",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'client_id' => '<string>',
'sender' => '<string>',
'timestamp' => 123,
'salt' => '<string>',
'signature' => '<string>',
'params' => [
'amount' => 123,
'mobile' => '<string>',
'country_id' => 123,
'currency' => '<string>',
'payment_method_type' => '<string>',
'callback_url' => '<string>',
'third_party_reference_1' => '<string>',
'third_party_reference_2' => '<string>',
'sub_merchant_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'first_name' => '<string>',
'last_name' => '<string>',
'account_name' => '<string>',
'bank_code' => '<string>',
'successful_url' => '<string>',
'failure_url' => '<string>',
'id_number' => '<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://sandbox.users.niobi.co/api/v4/niobi-unified-collections"
payload := strings.NewReader("{\n \"client_id\": \"<string>\",\n \"sender\": \"<string>\",\n \"timestamp\": 123,\n \"salt\": \"<string>\",\n \"signature\": \"<string>\",\n \"params\": {\n \"amount\": 123,\n \"mobile\": \"<string>\",\n \"country_id\": 123,\n \"currency\": \"<string>\",\n \"payment_method_type\": \"<string>\",\n \"callback_url\": \"<string>\",\n \"third_party_reference_1\": \"<string>\",\n \"third_party_reference_2\": \"<string>\",\n \"sub_merchant_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"account_name\": \"<string>\",\n \"bank_code\": \"<string>\",\n \"successful_url\": \"<string>\",\n \"failure_url\": \"<string>\",\n \"id_number\": \"<string>\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://sandbox.users.niobi.co/api/v4/niobi-unified-collections")
.header("Content-Type", "application/json")
.body("{\n \"client_id\": \"<string>\",\n \"sender\": \"<string>\",\n \"timestamp\": 123,\n \"salt\": \"<string>\",\n \"signature\": \"<string>\",\n \"params\": {\n \"amount\": 123,\n \"mobile\": \"<string>\",\n \"country_id\": 123,\n \"currency\": \"<string>\",\n \"payment_method_type\": \"<string>\",\n \"callback_url\": \"<string>\",\n \"third_party_reference_1\": \"<string>\",\n \"third_party_reference_2\": \"<string>\",\n \"sub_merchant_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"account_name\": \"<string>\",\n \"bank_code\": \"<string>\",\n \"successful_url\": \"<string>\",\n \"failure_url\": \"<string>\",\n \"id_number\": \"<string>\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://sandbox.users.niobi.co/api/v4/niobi-unified-collections")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"client_id\": \"<string>\",\n \"sender\": \"<string>\",\n \"timestamp\": 123,\n \"salt\": \"<string>\",\n \"signature\": \"<string>\",\n \"params\": {\n \"amount\": 123,\n \"mobile\": \"<string>\",\n \"country_id\": 123,\n \"currency\": \"<string>\",\n \"payment_method_type\": \"<string>\",\n \"callback_url\": \"<string>\",\n \"third_party_reference_1\": \"<string>\",\n \"third_party_reference_2\": \"<string>\",\n \"sub_merchant_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"account_name\": \"<string>\",\n \"bank_code\": \"<string>\",\n \"successful_url\": \"<string>\",\n \"failure_url\": \"<string>\",\n \"id_number\": \"<string>\"\n }\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"message": "We've sent a verification prompt to your phone. Please approve it to complete your payment.",
"data": {
"account_name": "Test Account",
"amount": 500,
"callback_url": "https://example.com/callback",
"country_id": 1,
"currency": "KES",
"mobile": "254161166649",
"name": "John Doe",
"payment_method_type": "send money",
"third_party_reference_1": "REF-001",
"third_party_reference_2": "REF-002",
"MerchantRequestID": "NIO-S0000001",
"depositId": "NIO-S0000001",
"account_number": null,
"bank_name": null,
"bank_code": null,
"status": "success",
"payment_token": null
},
"status_code": "000000"
}{
"success": false,
"message": "Niobi user not found or Country was not found or Transaction was not created!"
}{
"success": false,
"message": "Entity integration record not found or Client id not matched or Request was not verified."
}{
"success": false,
"message": "User not found!"
}Unified Collections
One endpoint for all collection methods across all supported markets. Accepts mobile money, bank transfer, virtual accounts, and redirect-based rails through a single unified interface. Each request must carry a freshly generated signature, signatures cannot be reused across requests. Conditional params fields vary by payment method and country. See the Collections Guide and Payment Methods for the full field matrix per market.
curl --request POST \
--url https://sandbox.users.niobi.co/api/v4/niobi-unified-collections \
--header 'Content-Type: application/json' \
--data '
{
"client_id": "<string>",
"sender": "<string>",
"timestamp": 123,
"salt": "<string>",
"signature": "<string>",
"params": {
"amount": 123,
"mobile": "<string>",
"country_id": 123,
"currency": "<string>",
"payment_method_type": "<string>",
"callback_url": "<string>",
"third_party_reference_1": "<string>",
"third_party_reference_2": "<string>",
"sub_merchant_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"first_name": "<string>",
"last_name": "<string>",
"account_name": "<string>",
"bank_code": "<string>",
"successful_url": "<string>",
"failure_url": "<string>",
"id_number": "<string>"
}
}
'import requests
url = "https://sandbox.users.niobi.co/api/v4/niobi-unified-collections"
payload = {
"client_id": "<string>",
"sender": "<string>",
"timestamp": 123,
"salt": "<string>",
"signature": "<string>",
"params": {
"amount": 123,
"mobile": "<string>",
"country_id": 123,
"currency": "<string>",
"payment_method_type": "<string>",
"callback_url": "<string>",
"third_party_reference_1": "<string>",
"third_party_reference_2": "<string>",
"sub_merchant_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"first_name": "<string>",
"last_name": "<string>",
"account_name": "<string>",
"bank_code": "<string>",
"successful_url": "<string>",
"failure_url": "<string>",
"id_number": "<string>"
}
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
client_id: '<string>',
sender: '<string>',
timestamp: 123,
salt: '<string>',
signature: '<string>',
params: {
amount: 123,
mobile: '<string>',
country_id: 123,
currency: '<string>',
payment_method_type: '<string>',
callback_url: '<string>',
third_party_reference_1: '<string>',
third_party_reference_2: '<string>',
sub_merchant_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
first_name: '<string>',
last_name: '<string>',
account_name: '<string>',
bank_code: '<string>',
successful_url: '<string>',
failure_url: '<string>',
id_number: '<string>'
}
})
};
fetch('https://sandbox.users.niobi.co/api/v4/niobi-unified-collections', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://sandbox.users.niobi.co/api/v4/niobi-unified-collections",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'client_id' => '<string>',
'sender' => '<string>',
'timestamp' => 123,
'salt' => '<string>',
'signature' => '<string>',
'params' => [
'amount' => 123,
'mobile' => '<string>',
'country_id' => 123,
'currency' => '<string>',
'payment_method_type' => '<string>',
'callback_url' => '<string>',
'third_party_reference_1' => '<string>',
'third_party_reference_2' => '<string>',
'sub_merchant_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'first_name' => '<string>',
'last_name' => '<string>',
'account_name' => '<string>',
'bank_code' => '<string>',
'successful_url' => '<string>',
'failure_url' => '<string>',
'id_number' => '<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://sandbox.users.niobi.co/api/v4/niobi-unified-collections"
payload := strings.NewReader("{\n \"client_id\": \"<string>\",\n \"sender\": \"<string>\",\n \"timestamp\": 123,\n \"salt\": \"<string>\",\n \"signature\": \"<string>\",\n \"params\": {\n \"amount\": 123,\n \"mobile\": \"<string>\",\n \"country_id\": 123,\n \"currency\": \"<string>\",\n \"payment_method_type\": \"<string>\",\n \"callback_url\": \"<string>\",\n \"third_party_reference_1\": \"<string>\",\n \"third_party_reference_2\": \"<string>\",\n \"sub_merchant_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"account_name\": \"<string>\",\n \"bank_code\": \"<string>\",\n \"successful_url\": \"<string>\",\n \"failure_url\": \"<string>\",\n \"id_number\": \"<string>\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://sandbox.users.niobi.co/api/v4/niobi-unified-collections")
.header("Content-Type", "application/json")
.body("{\n \"client_id\": \"<string>\",\n \"sender\": \"<string>\",\n \"timestamp\": 123,\n \"salt\": \"<string>\",\n \"signature\": \"<string>\",\n \"params\": {\n \"amount\": 123,\n \"mobile\": \"<string>\",\n \"country_id\": 123,\n \"currency\": \"<string>\",\n \"payment_method_type\": \"<string>\",\n \"callback_url\": \"<string>\",\n \"third_party_reference_1\": \"<string>\",\n \"third_party_reference_2\": \"<string>\",\n \"sub_merchant_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"account_name\": \"<string>\",\n \"bank_code\": \"<string>\",\n \"successful_url\": \"<string>\",\n \"failure_url\": \"<string>\",\n \"id_number\": \"<string>\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://sandbox.users.niobi.co/api/v4/niobi-unified-collections")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"client_id\": \"<string>\",\n \"sender\": \"<string>\",\n \"timestamp\": 123,\n \"salt\": \"<string>\",\n \"signature\": \"<string>\",\n \"params\": {\n \"amount\": 123,\n \"mobile\": \"<string>\",\n \"country_id\": 123,\n \"currency\": \"<string>\",\n \"payment_method_type\": \"<string>\",\n \"callback_url\": \"<string>\",\n \"third_party_reference_1\": \"<string>\",\n \"third_party_reference_2\": \"<string>\",\n \"sub_merchant_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"first_name\": \"<string>\",\n \"last_name\": \"<string>\",\n \"account_name\": \"<string>\",\n \"bank_code\": \"<string>\",\n \"successful_url\": \"<string>\",\n \"failure_url\": \"<string>\",\n \"id_number\": \"<string>\"\n }\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"message": "We've sent a verification prompt to your phone. Please approve it to complete your payment.",
"data": {
"account_name": "Test Account",
"amount": 500,
"callback_url": "https://example.com/callback",
"country_id": 1,
"currency": "KES",
"mobile": "254161166649",
"name": "John Doe",
"payment_method_type": "send money",
"third_party_reference_1": "REF-001",
"third_party_reference_2": "REF-002",
"MerchantRequestID": "NIO-S0000001",
"depositId": "NIO-S0000001",
"account_number": null,
"bank_name": null,
"bank_code": null,
"status": "success",
"payment_token": null
},
"status_code": "000000"
}{
"success": false,
"message": "Niobi user not found or Country was not found or Transaction was not created!"
}{
"success": false,
"message": "Entity integration record not found or Client id not matched or Request was not verified."
}{
"success": false,
"message": "User not found!"
}Integration Quick Reference
| Requirement | Standard | Rule / Action |
|---|---|---|
| Settlement Rule | payment_step: 2 only | Never credit goods or user accounts on payment_step: 1 (initiation). Only credit upon receiving payment_step: 2 with status: "success" (statusCode: "000000"). |
| Signatures | Fresh per request | Compute a fresh SHA-256 signature for every API call. Signatures cannot be reused. See Signature Generation. |
| Amount Format | Whole integers | Pass whole numbers in major currency units (for example, 1000 KES). No decimals allowed. |
| Idempotency | Unique merchant ref | Pass a distinct third_party_reference_1 per request to prevent duplicate charges. See the Idempotency Guide. |
| Delayed Webhook | Query status | If a callback is delayed, query the Get Transaction Status API before taking action. |
| Sandbox Testing | Mapped test MSISDNs | Use designated Sandbox Test Numbers mapped to outcomes (success, timeout, failed) instead of real phone numbers. |
| Production Prep | Technical checklist | Verify public HTTPS callback_url, country limits, and credentials in the Go-Live Checklist. |
List of Supported Countries
| Country Id | Country | Code | Currency | Payment Methods | Status |
|---|---|---|---|---|---|
| 1 | Kenya | 254 | KES | send money, Airtel | Active |
| 2 | Benin | 229 | XOF | Mtn, Moov | Active |
| 3 | Cote D’Ivoire | 225 | XOF | Mtn,Orange, Moov, Wave | Active |
| 4 | Cameroon | 237 | XAF | Mtn, Orange | Active |
| 5 | DRC | 243 | CDF | Airtel, Orange, Vodacom | Active |
| 8 | Rwanda | 250 | RWF | Mtn, Airtel | Active |
| 9 | Senegal | 221 | XOF | Orange, Free, Wave | Active |
| 10 | Tanzania | 255 | TZS | Airtel, Halotel, Tigo, Vodacom | Active |
| 11 | Uganda | 256 | UGX | Mtn, Airtel | Active |
| 12 | Zambia | 260 | ZMW | Mtn, Zamtel, Airtel | Active |
| 13 | Sierra Leone | 232 | SLE | Orange | Active |
| 15 | South Africa | 27 | ZAR | bank_eft, capitec | Active |
| 16 | Ghana | 233 | GHS | Mtn, At(AirtelTigo), Vodafone | Active |
| 17 | Nigeria | 234 | NGN | virtual_account | Active |
| 19 | Burkina Faso | 226 | XOF | Orange, Moov | Active |
| 18 | Mali | 223 | XOF | Orange, Moov | Active |
| 20 | Togo | 228 | XOF | moov, tmoney | Active |
| Country | Code | Currency | Payment Methods |
|---|---|---|---|
| Ethiopia | 251 | ETB | teleBirr, mBirr |
| Malawi | 265 | MWK | Airtel, tnm |
| Egypt | 20 | EGP | Vodafone, Orange |
| Mozambique | 258 | MZN | Vodacom, Movitel, Tmcel |
NGN Virtual Account - Supported Bank Codes
Pass one of the following codes asbank_code in your request to select the bank that will issue the virtual account.
| Bank Code | Bank Name |
|---|---|
| 17001 | 78 Finance Company Limited |
| 17002 | Fidelity Bank Limited |
| 17003 | Globus Bank Limited |
Special Collection Flows
Some corridors don’t follow the standard STK-push flow above: they require redirect URLs, customer national ID numbers, dynamic virtual bank accounts, or a secondary OTP verification step. For complete architectural walkthroughs, UX screenshots, and lifecycle diagrams, see:- Collection Methods Guide: Step-by-step guides for all payment collection channels.
- Country Guides Index: Country-specific corridors, telco channel rules, and test MSISDNs.
"eft" payment method value. South African EFT collections use payment_method_type: "bank_eft", shown below.- Bank EFT (South Africa)
- Capitec Collections (South Africa)
- Wave (Senegal & Côte d'Ivoire)
- OTP-Verified (Côte d'Ivoire Orange)
- Virtual Account (Nigeria)
payment_method_type: "bank_eft"Returns an authorization_url where the customer approves the payment directly with their bank (ABSA, FNB, Standard Bank, Nedbank, Investec, Bidvest, TymeBank).Documentation & Guides:{
"client_id": "YOUR_CLIENT_ID",
"sender": "YOUR_INTEGRATION_NAME",
"timestamp": 1724835600,
"salt": "random_salt_12345",
"signature": "c6b98e1f5d6a7890bc4e123456789abcdef0123456789abcdef0123456789abc",
"params": {
"amount": 500,
"currency": "ZAR",
"country_id": 15,
"mobile": "2763456789",
"name": "John Doe",
"bank_code": "0001",
"payment_method_type": "bank_eft",
"successful_url": "https://yourdomain.com/payment-success",
"failure_url": "https://yourdomain.com/payment-failed",
"callback_url": "https://yourdomain.com/niobi/callback",
"third_party_reference_1": "ZAR-DEPOSIT-01"
}
}
"authorization_url": "https://..." and "payment_step": 1. Redirect the customer to this URL. For a full walkthrough of redirect handling and bank selection, see the South Africa Country Guide.payment_method_type: "capitec"Capitec Bank collections authenticate directly against the payer’s national identity. You must pass the customer’s full 13-digit South African ID number in params.id_number (e.g. "9001015800083") for the request to be valid.Documentation & Guides:{
"client_id": "YOUR_CLIENT_ID",
"sender": "YOUR_INTEGRATION_NAME",
"timestamp": 1724835600,
"salt": "random_salt_12345",
"signature": "c6b98e1f5d6a7890bc4e123456789abcdef0123456789abcdef0123456789abc",
"params": {
"amount": 500,
"currency": "ZAR",
"country_id": 15,
"mobile": "2763456789",
"name": "John Doe",
"id_number": "9001015800083",
"payment_method_type": "capitec",
"successful_url": "https://yourdomain.com/payment-success",
"failure_url": "https://yourdomain.com/payment-failed",
"callback_url": "https://yourdomain.com/niobi/callback",
"third_party_reference_1": "ZAR-CAPITEC-01"
}
}
payment_method_type: "Wave"Wave uses a redirect-based browser authorization flow, not a USSD push. You must include successful_url and failure_url. After initiating the collection, redirect the customer to the returned authorization_url to complete payment.Documentation & Guides:- Collection Methods: Wave Flow & UI Walkthrough
- Senegal Country Guide: Wave Checkout
- Côte d’Ivoire Country Guide: Wave Collections
{
"client_id": "YOUR_CLIENT_ID",
"sender": "YOUR_INTEGRATION_NAME",
"timestamp": 1724835600,
"salt": "random_salt_12345",
"signature": "c6b98e1f5d6a7890bc4e123456789abcdef0123456789abcdef0123456789abc",
"params": {
"amount": 5000,
"currency": "XOF",
"country_id": 9,
"mobile": "221763456789",
"payment_method_type": "Wave",
"successful_url": "https://yourdomain.com/checkout/success",
"failure_url": "https://yourdomain.com/checkout/cancel",
"callback_url": "https://yourdomain.com/niobi/callback",
"third_party_reference_1": "WAVE-SN-001"
}
}
payment_method_type: "Orange"Orange payins in Côte d’Ivoire require a two-step OTP process instead of a single request.Documentation & Guides:Step 1: Initiate. Submit the collection request as usual. The customer receives an SMS OTP, and the response returns a payment_token instead of a terminal status:{
"success": true,
"message": "We've sent a verification prompt to your phone. Please approve it to complete your payment.",
"data": {
"status": "success",
"amount": 2000,
"currency": "XOF",
"country_id": 3,
"payment_token": "NIO-S0000001",
"payment_step": 1
},
"status_code": "000000"
}
POST /api/v4/niobi-unified-process-transaction:{
"client_id": "YOUR_CLIENT_ID",
"sender": "YOUR_INTEGRATION_NAME",
"timestamp": 1724835600,
"salt": "random_salt_12345",
"signature": "3a3cf7f2e3ff8d4ee991d7e1323312887dfda8536b8c2e197d0d4ef764013658",
"params": {
"otp": "000000",
"payment_token": "NIO-S0000001"
}
}
000000 to simulate successful verification.payment_method_type: "virtual_account"Generates a dedicated NGN virtual account for the customer to pay into. account_name is required. Pass a bank_code from the NGN Virtual Account bank codes above to choose the issuing bank; first_name and last_name are optional customer details.Documentation & Guides:{
"client_id": "YOUR_CLIENT_ID",
"params": {
"account_name": "John Doe",
"amount": 10000,
"bank_code": "17001",
"callback_url": "https://example.com/callback",
"country_id": 17,
"currency": "NGN",
"first_name": "John",
"last_name": "Doe",
"mobile": "2349034567899",
"payment_method_type": "virtual_account",
"third_party_reference_1": "TX-COLL-1001",
"third_party_reference_2": "CUST-501"
},
"salt": "random_salt_12345",
"sender": "YOUR_INTEGRATION_NAME",
"timestamp": 1724835600,
"signature": "a1b2c3d4e5f60718293a4b5c6d7e8f90123456789abcdef0123456789abcdef0"
}
account_number and bank_name for the generated virtual account, plus "payment_step": 1. Display these to the customer, this is the initial acknowledgement, not confirmation of payment. Await the terminal callback (payment_step: 2). See the full walkthrough in the Nigeria Country Guide.Body
Signed request envelope. All root fields except params are authentication fields.
Your Client ID from the Niobi Dashboard under Workspace > Integrations.
The exact title of your integration as registered in the Dashboard. Case-sensitive.
Unix timestamp in seconds at the time of signing. Must match the value used to generate the signature.
A unique random string you generate per request. Must be the same value used when generating the signature.
SHA-256 signature computed from this payload. Generate via the Signature endpoint or your own backend.
Show child attributes
Show child attributes
Response
Success
true
Explains the action payers need to take to complete the transaction. Wording varies by currency: NGN returns bank-transfer instructions, ZAR returns a payment link prompt, and every other currency returns the phone-approval prompt.
"We've sent a verification prompt to your phone. Please approve it to complete your payment. / Please use the following bank details for making a deposit via your preferred banking application (NGN) / Please use the link provided to finish your payment (ZAR)"
Show child attributes
Show child attributes
"000000"

