1Trust API
The 1Trust API has two products. Direct debit collects recurring payments from any Nigerian bank account: link a customer's account once, then charge any amount up to the limit. Identity verifies a customer's BVN or NIN. Each is switched on separately for your business.
All requests go to https://api.1trustmfb.com/v1 over HTTPS. Requests and responses are JSON. Amounts are in kobo (₦1 = 100 kobo).
How direct debit works
- Your server calls
POST /mandates. We register the mandate with the customer's bank. - We email the customer: send ₦50 from the mandated account to activate.
- The bank approves the mandate and we send you
mandate.active. - You call
POST /mandates/{code}/debitwhenever a payment is due. - We send
debit.successordebit.failed.
Authentication
Authenticate every request with your secret key in the Authorization header. Find your keys under API keys & webhooks in the dashboard.
Test keys start with sk_test_ and never move money. Live keys start with sk_live_ and move real money. Keep secret keys on your server only.
Authorization: Bearer sk_test_3b1f…c81e
Errors
Every response has a boolean status and a human-readable message. Failed requests also return an error.code you can branch on.
| HTTP | Meaning |
|---|---|
| 200 / 201 | Request succeeded |
| 400 | A field is missing or invalid |
| 401 | Missing or wrong secret key |
| 404 | Mandate or debit not found |
| 409 | Duplicate reference, or a second debit on the same day |
| 422 | The customer's bank declined the request (see error.code) |
| 5xx | Something went wrong on our side. Retry with the same reference. |
| error.code | What to do |
|---|---|
| missing_field | A required field is missing; error.field names it |
| invalid_phone | Phone must be 11 digits, starting with 0 |
| invalid_account_number | Account number must be 10 digits |
| invalid_email | Send a valid email address |
| invalid_reference | Use 1–50 letters, numbers, underscores or hyphens |
| already_debited_today | One debit per mandate per day. Try again tomorrow |
| customer_exists | A customer with this email is already saved; fetch it instead |
| customer_id_taken | Your customer ID is already used by another customer |
| live_not_enabled | Use your test keys until 1Trust approves Live |
| merchant_suspended | Contact 1Trust support |
| mandate_cooling_off | First charge is allowed 6 hours after activation |
| amount_below_minimum | Minimum debit is ₦500 |
| mandate_not_active | Wait for the mandate.active webhook before debiting |
| amount_exceeds_mandate | Debit no more than the mandate amount |
| insufficient_funds | Retry on another day |
| account_name_mismatch | Check the account number and BVN belong to the same person |
| mandate_account_mismatch | The account or bank doesn't match the mandate |
| mandate_not_started | The mandate's start date is in the future. Charge on or after the start date. |
| limit_exceeded | On a debit: the customer's transfer limit or withdrawal frequency is reached. On mandate create: your daily mandate limit is reached; try tomorrow or contact 1Trust |
| status_unknown | The debit isn't confirmed yet. We check again and send a webhook; don't retry with a new reference |
| invalid_bvn | BVN must be 11 digits and exist |
| invalid_nin_sharecode | Ask the customer for a fresh ShareCode from the NINAuth app |
| consent_declined | The customer said no. Start a new verification if they change their mind |
| verification_unavailable | Temporarily unavailable. Retry in a few minutes; you aren't billed |
| consent_required | A BVN Full check needs the customer's consent. Request consent, or pass a consent_code |
| sandbox_limit_reached | Test mode only: the allowance of test checks against the real verification service is used up. Contact 1Trust |
| invalid_date | Send dates as YYYY-MM-DD (Lagos days, both ends included), with from on or before to |
| idempotency_key_reused | This Idempotency-Key was used with a different request. Use a new key |
| idempotency_in_progress | The first request with this key is still running. Retry shortly with the same key |
| delivery_in_progress | The webhook delivery is being sent right now. Check its status in a minute |
| conflict | The request clashed with another one at the same moment. Retry |
| rate_limited | Too many requests (for example, more than 10 test webhooks a minute). Wait and retry |
| module_not_enabled | This product isn't switched on for your business. Ask 1Trust to enable it |
{
"status": false,
"message": "Mandate is not active yet",
"error": {
"code": "mandate_not_active",
"field": null
}
}Pagination
List endpoints return 50 records per page by default. Pass page and per_page (up to 100). The response includes a meta object so you know when to stop.
{
"status": true,
"message": "Mandates retrieved",
"data": [
"…"
],
"meta": {
"total": 236,
"page": 2,
"per_page": 50,
"page_count": 5
}
}Rate limits
Each secret key can make up to 20 requests per second. Above that you get HTTP 429; wait a second and retry. Separately, each mandate can be debited once per day.
Test mode
Use test keys to build your integration. Test mandates move to pending_activation straight away. Use these account numbers to trigger outcomes:
| Account number | Result |
|---|---|
| 0000000001 | Mandate activates after 60 seconds |
| 0000000002 | Mandate rejected: account name mismatch |
| 0000000003 | Debits fail with insufficient_funds |
| Any other | Stays pending until you choose Simulate ₦50 transfer in the dashboard |
List customers
Returns your customers, newest first, with how many mandates each has, what you have collected from them and their latest identity check.
Query
Search name, email, phone or your customer ID
Defaults 1 and 50; max 100
curl https://api.1trustmfb.com/v1/customers?q=zainab \
-H "Authorization: Bearer sk_test_3b1f…c81e"const res = await fetch("https://api.1trustmfb.com/v1/customers?q=zainab", { method: "GET", headers: { Authorization: `Bearer ${process.env.ONETRUST_SECRET_KEY}`, }, }); const data = await res.json();
$ch = curl_init("https://api.1trustmfb.com/v1/customers?q=zainab"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("ONETRUST_SECRET_KEY"), "Content-Type: application/json", ], ]); $data = json_decode(curl_exec($ch), true);
import os, requests
res = requests.get(
"https://api.1trustmfb.com/v1/customers?q=zainab",
headers={"Authorization": f"Bearer {os.environ['ONETRUST_SECRET_KEY']}"},
)
data = res.json(){
"status": true,
"message": "Customers retrieved",
"data": [
{
"id": "cmuszz1wv000569urq2kfw9de",
"customer_id": "CUST-00931",
"name": "Zainab Abdullahi",
"email": "zainab.abdullahi@example.com",
"phone": "08023456789",
"address": "22 Gana Street, Maitama, Abuja",
"mandates": 1,
"active_mandates": 1,
"collected": 4500000,
"identity": {
"status": "verified",
"checked_at": "2026-10-01T08: 00: 02+01: 00"
},
"created_at": "2026-09-25T11: 30: 00+01: 00"
}
],
"meta": {
"total": 1,
"page": 1,
"per_page": 50,
"page_count": 1
}
}Create customer
Saves a customer so you can pick them when you create a mandate. Each email, and each of your customer IDs, can be used once per mode.
Body
Full name as on the bank account
Customer email
11-digit Nigerian phone number
Customer address
Your ID for this customer, up to 40 characters
Errors
409 customer_exists when the email is already saved, 409 customer_id_taken when your customer ID is already used.
curl -X POST https://api.1trustmfb.com/v1/customers \ -H "Authorization: Bearer sk_test_3b1f…c81e" \ -H "Content-Type: application/json" \ -d '{ "name": "Zainab Abdullahi", "email": "zainab.abdullahi@example.com", "phone": "08023456789", "address": "22 Gana Street, Maitama, Abuja", "customer_id": "CUST-00931" }'
const res = await fetch("https://api.1trustmfb.com/v1/customers", { method: "POST", headers: { Authorization: `Bearer ${process.env.ONETRUST_SECRET_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ "name": "Zainab Abdullahi", "email": "zainab.abdullahi@example.com", "phone": "08023456789", "address": "22 Gana Street, Maitama, Abuja", "customer_id": "CUST-00931" }), }); const data = await res.json();
$ch = curl_init("https://api.1trustmfb.com/v1/customers"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("ONETRUST_SECRET_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "name" => "Zainab Abdullahi", "email" => "zainab.abdullahi@example.com", "phone" => "08023456789", "address" => "22 Gana Street, Maitama, Abuja", "customer_id" => "CUST-00931" ]), ]); $data = json_decode(curl_exec($ch), true);
import os, requests
res = requests.post(
"https://api.1trustmfb.com/v1/customers",
headers={"Authorization": f"Bearer {os.environ['ONETRUST_SECRET_KEY']}"},
json={
"name": "Zainab Abdullahi",
"email": "zainab.abdullahi@example.com",
"phone": "08023456789",
"address": "22 Gana Street, Maitama, Abuja",
"customer_id": "CUST-00931",
},
)
data = res.json(){
"status": true,
"message": "Customer created",
"data": {
"id": "cmuszz1wv000569urq2kfw9de",
"customer_id": "CUST-00931",
"name": "Zainab Abdullahi",
"email": "zainab.abdullahi@example.com",
"phone": "08023456789",
"address": "22 Gana Street, Maitama, Abuja",
"mandates": 0,
"active_mandates": 0,
"collected": 0,
"identity": null,
"created_at": "2026-09-25T11: 30: 00+01: 00"
}
}List banks
Returns banks whose customers can be debited by direct debit. Use the code as bank_code when creating a mandate.
curl https://api.1trustmfb.com/v1/banks \
-H "Authorization: Bearer sk_test_3b1f…c81e"const res = await fetch("https://api.1trustmfb.com/v1/banks", { method: "GET", headers: { Authorization: `Bearer ${process.env.ONETRUST_SECRET_KEY}`, }, }); const data = await res.json();
$ch = curl_init("https://api.1trustmfb.com/v1/banks"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("ONETRUST_SECRET_KEY"), "Content-Type: application/json", ], ]); $data = json_decode(curl_exec($ch), true);
import os, requests
res = requests.get(
"https://api.1trustmfb.com/v1/banks",
headers={"Authorization": f"Bearer {os.environ['ONETRUST_SECRET_KEY']}"},
)
data = res.json(){
"status": true,
"message": "Banks retrieved",
"data": [
{
"code": "044",
"name": "Access Bank"
},
{
"code": "058",
"name": "GTBank"
},
{
"code": "50515",
"name": "Moniepoint MFB"
}
]
}Create mandate
Registers the mandate and sends the customer activation instructions by email. The mandate starts as pending_activation. It becomes active once the customer sends ₦50 from the mandated account, usually within 2 hours.
Body
Account holder's full name
Where we send activation instructions
11-digit Nigerian phone number
Account holder's address
Your ID for this customer
10-digit NUBAN
Most you can debit at once, in kobo
YYYY-MM-DD. Mandate stops after this date.
What the customer is paying for. Shown on their activation message.
Your unique ID for this mandate
curl -X POST https://api.1trustmfb.com/v1/mandates \ -H "Authorization: Bearer sk_test_3b1f…c81e" \ -H "Content-Type: application/json" \ -d '{ "customer": { "name": "Zainab Abdullahi", "email": "zainab.abdullahi@example.com", "phone": "08023456789", "address": "22 Gana Street, Maitama, Abuja", "id": "CUST-00931" }, "bank_code": "058", "account_number": "0145678923", "max_amount": 4500000, "end_date": "2027-09-30", "description": "Loan repayment", "reference": "MND-REF-00931" }'
const res = await fetch("https://api.1trustmfb.com/v1/mandates", { method: "POST", headers: { Authorization: `Bearer ${process.env.ONETRUST_SECRET_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ "customer": { "name": "Zainab Abdullahi", "email": "zainab.abdullahi@example.com", "phone": "08023456789", "address": "22 Gana Street, Maitama, Abuja", "id": "CUST-00931" }, "bank_code": "058", "account_number": "0145678923", "max_amount": 4500000, "end_date": "2027-09-30", "description": "Loan repayment", "reference": "MND-REF-00931" }), }); const data = await res.json();
$ch = curl_init("https://api.1trustmfb.com/v1/mandates"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("ONETRUST_SECRET_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "customer" => [ "name" => "Zainab Abdullahi", "email" => "zainab.abdullahi@example.com", "phone" => "08023456789", "address" => "22 Gana Street, Maitama, Abuja", "id" => "CUST-00931" ], "bank_code" => "058", "account_number" => "0145678923", "max_amount" => 4500000, "end_date" => "2027-09-30", "description" => "Loan repayment", "reference" => "MND-REF-00931" ]), ]); $data = json_decode(curl_exec($ch), true);
import os, requests
res = requests.post(
"https://api.1trustmfb.com/v1/mandates",
headers={"Authorization": f"Bearer {os.environ['ONETRUST_SECRET_KEY']}"},
json={
"customer": {
"name": "Zainab Abdullahi",
"email": "zainab.abdullahi@example.com",
"phone": "08023456789",
"address": "22 Gana Street, Maitama, Abuja",
"id": "CUST-00931",
},
"bank_code": "058",
"account_number": "0145678923",
"max_amount": 4500000,
"end_date": "2027-09-30",
"description": "Loan repayment",
"reference": "MND-REF-00931",
},
)
data = res.json(){
"status": true,
"message": "Mandate created. Awaiting customer activation.",
"data": {
"mandate_code": "MND_1052X7QA",
"mandate_reference": "9516439/1/5218796181",
"status": "pending_activation",
"customer": {
"name": "Zainab Abdullahi",
"email": "zainab.abdullahi@example.com",
"phone": "08023456789"
},
"bank_code": "058",
"account_number": "0145678923",
"account_name": "ZAINAB ABDULLAHI",
"max_amount": 4500000,
"description": "Loan repayment",
"start_date": "2026-09-25",
"end_date": "2027-09-30",
"activation": {
"amount": 5000,
"account_number": "9880218357",
"bank_name": "Titan-Paystack",
"instructions": "Pay ₦50 from account 0145678923 to 9880218357 (Titan-Paystack) to activate your mandate.",
"expires_at": "2026-10-02T10: 00: 00+01: 00"
},
"reference": "MND-REF-00931",
"created_at": "2026-09-25T10: 00: 00+01: 00"
}
}List mandates
Returns your mandates, newest first.
Query
pending_activation, active, cancelled, expired or rejected
Filter by customer email
Created between these dates
Defaults 1 and 50; max 100
curl https://api.1trustmfb.com/v1/mandates?status=active&per_page=20 \
-H "Authorization: Bearer sk_test_3b1f…c81e"const res = await fetch("https://api.1trustmfb.com/v1/mandates?status=active&per_page=20", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.ONETRUST_SECRET_KEY}`,
},
});
const data = await res.json();$ch = curl_init("https://api.1trustmfb.com/v1/mandates?status=active&per_page=20");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("ONETRUST_SECRET_KEY"),
"Content-Type: application/json",
],
]);
$data = json_decode(curl_exec($ch), true);import os, requests
res = requests.get(
"https://api.1trustmfb.com/v1/mandates?status=active&per_page=20",
headers={"Authorization": f"Bearer {os.environ['ONETRUST_SECRET_KEY']}"},
)
data = res.json()Fetch mandate
Returns one mandate with its current status and, if still pending, the activation instructions.
Mandate statuses
| Status | Meaning |
|---|---|
| pending_activation | Registered. Waiting for the customer's ₦50 transfer. |
| active | Ready to debit |
| rejected | Bank declined, often a BVN or name mismatch |
| expired | No ₦50 transfer within 7 days |
| cancelled | Stopped by you or the customer |
| completed | End date passed |
curl https://api.1trustmfb.com/v1/mandates/MND_1052X7QA \
-H "Authorization: Bearer sk_test_3b1f…c81e"const res = await fetch("https://api.1trustmfb.com/v1/mandates/MND_1052X7QA", { method: "GET", headers: { Authorization: `Bearer ${process.env.ONETRUST_SECRET_KEY}`, }, }); const data = await res.json();
$ch = curl_init("https://api.1trustmfb.com/v1/mandates/MND_1052X7QA"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("ONETRUST_SECRET_KEY"), "Content-Type: application/json", ], ]); $data = json_decode(curl_exec($ch), true);
import os, requests
res = requests.get(
"https://api.1trustmfb.com/v1/mandates/MND_1052X7QA",
headers={"Authorization": f"Bearer {os.environ['ONETRUST_SECRET_KEY']}"},
)
data = res.json()Pause or resume a mandate
Pausing suspends the mandate so no debits can go through. Call POST /mandates/{mandate_code}/resume to make it active again. Neither call needs a body.
curl -X POST https://api.1trustmfb.com/v1/mandates/MND_1052X7QA/pause \
-H "Authorization: Bearer sk_test_3b1f…c81e"const res = await fetch("https://api.1trustmfb.com/v1/mandates/MND_1052X7QA/pause", { method: "POST", headers: { Authorization: `Bearer ${process.env.ONETRUST_SECRET_KEY}`, }, }); const data = await res.json();
$ch = curl_init("https://api.1trustmfb.com/v1/mandates/MND_1052X7QA/pause"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("ONETRUST_SECRET_KEY"), "Content-Type: application/json", ], ]); $data = json_decode(curl_exec($ch), true);
import os, requests
res = requests.post(
"https://api.1trustmfb.com/v1/mandates/MND_1052X7QA/pause",
headers={"Authorization": f"Bearer {os.environ['ONETRUST_SECRET_KEY']}"},
)
data = res.json()Cancel mandate
Stops all future debits. This can't be undone. Create a new mandate to start again.
Stored for your records
curl -X POST https://api.1trustmfb.com/v1/mandates/MND_1052X7QA/cancel \ -H "Authorization: Bearer sk_test_3b1f…c81e" \ -H "Content-Type: application/json" \ -d '{ "reason": "Customer closed account" }'
const res = await fetch("https://api.1trustmfb.com/v1/mandates/MND_1052X7QA/cancel", { method: "POST", headers: { Authorization: `Bearer ${process.env.ONETRUST_SECRET_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ "reason": "Customer closed account" }), }); const data = await res.json();
$ch = curl_init("https://api.1trustmfb.com/v1/mandates/MND_1052X7QA/cancel"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("ONETRUST_SECRET_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "reason" => "Customer closed account" ]), ]); $data = json_decode(curl_exec($ch), true);
import os, requests
res = requests.post(
"https://api.1trustmfb.com/v1/mandates/MND_1052X7QA/cancel",
headers={"Authorization": f"Bearer {os.environ['ONETRUST_SECRET_KEY']}"},
json={
"reason": "Customer closed account",
},
)
data = res.json()Debit mandate
Charges an active mandate, at least 6 hours after it became active. Amount: ₦500 up to the mandate's max, one debit per mandate per day. We deduct ₦107.50 (₦100 + VAT) and pay you the rest at 3:00pm the same day (debits before 2:00pm) or 9:00am the next business day. We confirm the final result with a debit.success or debit.failed webhook.
In kobo, up to the mandate's max_amount. Any amount, any day.
Your unique reference. Reuse it to retry safely.
Up to 50 characters. Defaults to your business name + "via 1Trust".
curl -X POST https://api.1trustmfb.com/v1/mandates/MND_1052X7QA/debit \ -H "Authorization: Bearer sk_test_3b1f…c81e" \ -H "Content-Type: application/json" \ -d '{ "amount": 4500000, "reference": "ORD_7H2KD91Q", "narration": "October instalment" }'
const res = await fetch("https://api.1trustmfb.com/v1/mandates/MND_1052X7QA/debit", { method: "POST", headers: { Authorization: `Bearer ${process.env.ONETRUST_SECRET_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ "amount": 4500000, "reference": "ORD_7H2KD91Q", "narration": "October instalment" }), }); const data = await res.json();
$ch = curl_init("https://api.1trustmfb.com/v1/mandates/MND_1052X7QA/debit"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("ONETRUST_SECRET_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "amount" => 4500000, "reference" => "ORD_7H2KD91Q", "narration" => "October instalment" ]), ]); $data = json_decode(curl_exec($ch), true);
import os, requests
res = requests.post(
"https://api.1trustmfb.com/v1/mandates/MND_1052X7QA/debit",
headers={"Authorization": f"Bearer {os.environ['ONETRUST_SECRET_KEY']}"},
json={
"amount": 4500000,
"reference": "ORD_7H2KD91Q",
"narration": "October instalment",
},
)
data = res.json(){
"status": true,
"message": "Debit successful",
"data": {
"reference": "ORD_7H2KD91Q",
"mandate_code": "MND_1052X7QA",
"amount": 4500000,
"fee": 10750,
"net": 4489250,
"status": "success",
"narration": "October instalment",
"session_id": "999999260925100455123456789012",
"created_at": "2026-10-01T09: 12: 44+01: 00"
}
}Retry activation
For a pending_activation mandate: resends the ₦50 instructions to the customer and rechecks its status. Pass mandate_codes to POST /mandates/retry-activation to retry many at once.
curl -X POST https://api.1trustmfb.com/v1/mandates/MND_1052X7QA/retry-activation \
-H "Authorization: Bearer sk_test_3b1f…c81e"const res = await fetch("https://api.1trustmfb.com/v1/mandates/MND_1052X7QA/retry-activation", { method: "POST", headers: { Authorization: `Bearer ${process.env.ONETRUST_SECRET_KEY}`, }, }); const data = await res.json();
$ch = curl_init("https://api.1trustmfb.com/v1/mandates/MND_1052X7QA/retry-activation"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("ONETRUST_SECRET_KEY"), "Content-Type: application/json", ], ]); $data = json_decode(curl_exec($ch), true);
import os, requests
res = requests.post(
"https://api.1trustmfb.com/v1/mandates/MND_1052X7QA/retry-activation",
headers={"Authorization": f"Bearer {os.environ['ONETRUST_SECRET_KEY']}"},
)
data = res.json()Fetch debit
Returns a debit by your reference. Use it to confirm the result if you missed a webhook. List all debits with GET /debits, which takes the same filters as List mandates.
curl https://api.1trustmfb.com/v1/debits/ORD_7H2KD91Q \
-H "Authorization: Bearer sk_test_3b1f…c81e"const res = await fetch("https://api.1trustmfb.com/v1/debits/ORD_7H2KD91Q", { method: "GET", headers: { Authorization: `Bearer ${process.env.ONETRUST_SECRET_KEY}`, }, }); const data = await res.json();
$ch = curl_init("https://api.1trustmfb.com/v1/debits/ORD_7H2KD91Q"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("ONETRUST_SECRET_KEY"), "Content-Type: application/json", ], ]); $data = json_decode(curl_exec($ch), true);
import os, requests
res = requests.get(
"https://api.1trustmfb.com/v1/debits/ORD_7H2KD91Q",
headers={"Authorization": f"Bearer {os.environ['ONETRUST_SECRET_KEY']}"},
)
data = res.json()List payouts
Two payouts each business day, minus fees: 3:00pm for debits made between 2:00am and 1:59pm, and 9:00am the next business day for the rest. Weekend debits are paid Monday 9:00am. Each payout lists the debits it covers.
{
"status": true,
"message": "Payouts retrieved",
"data": [
{
"id": "PO_20260926",
"date": "2026-09-26",
"debits": 3,
"collected": 21500000,
"fees": 32250,
"amount": 21467750,
"status": "paid",
"account": {
"bank_code": "058",
"account_number": "0123456780"
}
}
]
}Identity verification
Verify who a customer is with their BVN or NIN. Choose a check:
| Check | What you send | What you get |
|---|---|---|
| BVN · Full | BVN | The customer approves first (OTP, USSD or face). Then: name, date of birth, gender, phone and photo. |
| BVN · Match | BVN + the details you hold | Yes/no for each detail. No approval step. |
| NIN · Full | NIN ShareCode | Name, date of birth, gender, phone and photo. The ShareCode is the customer's consent. |
| NIN · Match | ShareCode + the details you hold | Yes/no for each detail. |
What your customer needs
| Check | Customer does |
|---|---|
| BVN · Full | Approves when we ask (OTP, USSD or face) by the consent link. If they already approved and have a consent code, send it as consent_code and we verify at once. |
| NIN | Opens the NINAuth app (Play Store or App Store), signs in with their NIN and generates a 6-character ShareCode for you. |
You are billed only for checks that return a result: verified, partial_match, or failed with details_do_not_match. A customer's approval for BVN · Full stays valid until consent_expires_at, so repeat checks don't ask again.
Test numbers
| BVN | Result |
|---|---|
| 22222222222 | Verifies (Full: after you choose Simulate consent) |
| 22222222223 | Customer declines consent |
| 22222222224 | Match: phone doesn't match |
| Any test BVN + consent code 4878BF48BD6FD92C | Full: verifies at once |
| ShareCode 000000 | NIN: fails with invalid_nin_sharecode; any other ShareCode verifies |
Start verification
For BVN · Full without a consent_code, the verification starts as awaiting_consent. With customer_present: true you get a consent_url to send the customer to; otherwise we email them a link. You get verification.verified when it completes. Every other check returns its result straight away.
Body
bvn or nin
full or match
11-digit BVN, or the 6-character NIN ShareCode
Where we send the consent link
11-digit Nigerian phone number
BVN · Full only. Default true
Where the customer lands after approving. Defaults to your setting
BVN · Full. The customer already approved and gave you a code: we skip the request and verify at once
NIN only, required. Why you need the data, e.g. financialProducts, creditBackgroundCheck, insurance, employmentRecruitment. Default financialProducts
Required for Match
YYYY-MM-DD. Required for Match
male or female. Required for Match
curl -X POST https://api.1trustmfb.com/v1/verifications \ -H "Authorization: Bearer sk_test_3b1f…c81e" \ -H "Content-Type: application/json" \ -d '{ "type": "bvn", "method": "full", "number": "22222222222", "customer": { "name": "Zainab Abdullahi", "email": "zainab.abdullahi@example.com", "phone": "08023456789" }, "customer_present": true }'
const res = await fetch("https://api.1trustmfb.com/v1/verifications", { method: "POST", headers: { Authorization: `Bearer ${process.env.ONETRUST_SECRET_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ "type": "bvn", "method": "full", "number": "22222222222", "customer": { "name": "Zainab Abdullahi", "email": "zainab.abdullahi@example.com", "phone": "08023456789" }, "customer_present": true }), }); const data = await res.json();
$ch = curl_init("https://api.1trustmfb.com/v1/verifications"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("ONETRUST_SECRET_KEY"), "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "type" => "bvn", "method" => "full", "number" => "22222222222", "customer" => [ "name" => "Zainab Abdullahi", "email" => "zainab.abdullahi@example.com", "phone" => "08023456789" ], "customer_present" => true ]), ]); $data = json_decode(curl_exec($ch), true);
import os, requests
res = requests.post(
"https://api.1trustmfb.com/v1/verifications",
headers={"Authorization": f"Bearer {os.environ['ONETRUST_SECRET_KEY']}"},
json={
"type": "bvn",
"method": "full",
"number": "22222222222",
"customer": {
"name": "Zainab Abdullahi",
"email": "zainab.abdullahi@example.com",
"phone": "08023456789",
},
"customer_present": True,
},
)
data = res.json(){
"status": true,
"message": "Consent requested",
"data": {
"id": "VER_3112KQD",
"type": "bvn",
"method": "full",
"status": "awaiting_consent",
"customer": {
"name": "Zainab Abdullahi",
"email": "zainab.abdullahi@example.com",
"phone": "08023456789"
},
"number_last4": "2222",
"consent_url": "https://verify.1trustmfb.com/c/ver_3112kqd",
"fee": null,
"created_at": "2026-10-03T09: 12: 44+01: 00"
}
}Fetch verification
Returns one verification with its result. photo_url is signed and expires after 15 minutes.
Statuses
| Status | Meaning |
|---|---|
| awaiting_consent | Waiting for the customer to approve |
| verified | Record found; all details match |
| partial_match | Some details don't match. See match |
| failed | No detail matched, or the number is invalid. See error.code |
| consent_declined | Customer said no |
| expired | No approval within 24 hours |
curl https://api.1trustmfb.com/v1/verifications/VER_3112KQD \
-H "Authorization: Bearer sk_test_3b1f…c81e"const res = await fetch("https://api.1trustmfb.com/v1/verifications/VER_3112KQD", { method: "GET", headers: { Authorization: `Bearer ${process.env.ONETRUST_SECRET_KEY}`, }, }); const data = await res.json();
$ch = curl_init("https://api.1trustmfb.com/v1/verifications/VER_3112KQD"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("ONETRUST_SECRET_KEY"), "Content-Type: application/json", ], ]); $data = json_decode(curl_exec($ch), true);
import os, requests
res = requests.get(
"https://api.1trustmfb.com/v1/verifications/VER_3112KQD",
headers={"Authorization": f"Bearer {os.environ['ONETRUST_SECRET_KEY']}"},
)
data = res.json(){
"status": true,
"message": "Verification retrieved",
"data": {
"id": "VER_3112KQD",
"type": "bvn",
"method": "full",
"status": "verified",
"result": {
"first_name": "Zainab",
"middle_name": "Hadiza",
"last_name": "Abdullahi",
"dob": "1993-04-18",
"gender": "female",
"phone": "08023456789",
"photo_url": "https://files.1trustmfb.com/v/VER_3112KQD.jpg?exp=900"
},
"number_last4": "2222",
"consent_expires_at": "2026-11-02T23: 59: 59+01: 00",
"billable": true,
"completed_at": "2026-10-03T09: 14: 10+01: 00"
}
}{
"status": true,
"message": "Verification complete",
"data": {
"id": "VER_3115PLM",
"type": "bvn",
"method": "match",
"status": "partial_match",
"match": {
"first_name": true,
"middle_name": true,
"last_name": true,
"dob": true,
"gender": true,
"phone": false
},
"billable": true
}
}List verifications
Newest first.
Query
Any status above
bvn or nin
Created between these dates
Defaults 1 and 50; max 100
curl https://api.1trustmfb.com/v1/verifications?status=verified&type=bvn \
-H "Authorization: Bearer sk_test_3b1f…c81e"const res = await fetch("https://api.1trustmfb.com/v1/verifications?status=verified&type=bvn", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.ONETRUST_SECRET_KEY}`,
},
});
const data = await res.json();$ch = curl_init("https://api.1trustmfb.com/v1/verifications?status=verified&type=bvn");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("ONETRUST_SECRET_KEY"),
"Content-Type: application/json",
],
]);
$data = json_decode(curl_exec($ch), true);import os, requests
res = requests.get(
"https://api.1trustmfb.com/v1/verifications?status=verified&type=bvn",
headers={"Authorization": f"Bearer {os.environ['ONETRUST_SECRET_KEY']}"},
)
data = res.json()Resend consent link
For an awaiting_consent verification: emails the customer a fresh link.
curl -X POST https://api.1trustmfb.com/v1/verifications/VER_3112KQD/resend \
-H "Authorization: Bearer sk_test_3b1f…c81e"const res = await fetch("https://api.1trustmfb.com/v1/verifications/VER_3112KQD/resend", { method: "POST", headers: { Authorization: `Bearer ${process.env.ONETRUST_SECRET_KEY}`, }, }); const data = await res.json();
$ch = curl_init("https://api.1trustmfb.com/v1/verifications/VER_3112KQD/resend"); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . getenv("ONETRUST_SECRET_KEY"), "Content-Type: application/json", ], ]); $data = json_decode(curl_exec($ch), true);
import os, requests
res = requests.post(
"https://api.1trustmfb.com/v1/verifications/VER_3112KQD/resend",
headers={"Authorization": f"Bearer {os.environ['ONETRUST_SECRET_KEY']}"},
)
data = res.json()Webhooks
We send a POST to your webhook URL whenever a mandate, debit or verification changes. Reply with HTTP 200 within 10 seconds. We retry failed deliveries up to 5 times over 24 hours.
| Event | Sent when |
|---|---|
| mandate.created | Mandate registered |
| mandate.active | Customer sent ₦50. You can debit now. |
| mandate.rejected | Bank declined the mandate |
| mandate.expired | No activation within 7 days |
| mandate.cancelled | Mandate stopped |
| debit.success | Money collected |
| debit.failed | Debit declined, with a reason |
| payout.paid | Settlement sent to your bank account |
| verification.consent_granted | Customer approved a BVN · Full check |
| verification.verified | Result ready (verified or partial match) |
| verification.failed | Failed, declined or expired |
{
"event": "mandate.active",
"data": {
"mandate_code": "MND_1052X7QA",
"status": "active",
"account_number": "0145678923",
"bank_code": "058",
"amount": 4500000,
"reference": "CUST-00931",
"activated_at": "2026-09-25T11: 42: 10+01: 00"
}
}Verify signatures
Every webhook carries an x-1trust-signature header: an HMAC-SHA512 of the raw request body, keyed with the webhook signing secret (starts with whsec_, shown once when you save the webhook URL). Compute it yourself and reject the request if it doesn't match.
To also reject replays, use x-1trust-signature-v2: the HMAC-SHA512 of {x-1trust-timestamp}.{raw body}. Reject it when the timestamp is more than 5 minutes old. Each event also has a stable x-1trust-event-id across retries, so you can ignore duplicates.
import crypto from "node:crypto"; app.post("/webhooks/1trust", express.raw({ type: "*/*" }), (req, res) => { const hash = crypto .createHmac("sha512", process.env.ONETRUST_WEBHOOK_SECRET) .update(req.body) .digest("hex"); if (hash !== req.headers["x-1trust-signature"]) return res.sendStatus(401); const event = JSON.parse(req.body); // handle event.event, e.g. "debit.success" res.sendStatus(200); });
$body = file_get_contents("php://input"); $hash = hash_hmac("sha512", $body, getenv("ONETRUST_WEBHOOK_SECRET")); if (!hash_equals($hash, $_SERVER["HTTP_X_1TRUST_SIGNATURE"] ?? "")) { http_response_code(401); exit; } $event = json_decode($body, true); http_response_code(200);