Integrate with Mono's financial data API for account linking, transaction history, income verification, and identity checks across Nigeria and other African markets. Use this skill whenever the user wants to connect bank accounts, pull transaction data, verify income or identity, build a personal finance app, implement open banking, do credit scoring, or work with Mono's API. Also trigger for 'Mono', 'Mono Connect', 'open banking Nigeria', 'account aggregation Africa', 'bank data API', 'financial data API Africa', 'pull bank transactions', 'income verification Nigeria', or 'KYC bank data'.
Mono is Africa's leading open banking platform. It lets you securely connect to users' bank accounts to pull financial data — transactions, balances, income, identity, and spending analytics. Think of it as Plaid for Africa, operating across Nigeria, Ghana, Kenya, and South Africa with integrations across 30+ financial institutions.
You're building something that needs access to a user's bank data — a personal finance app, a lending platform that needs income verification, a credit scoring engine, an accounting tool that syncs with bank accounts, or any product that benefits from knowing a user's financial picture. Mono handles the bank connections so you don't have to integrate with each bank individually.
Mono uses a secret key for server-side requests:
mono-sec-key: live_sk_xxxxx
Sandbox: test_sk_xxxxx. Production: live_sk_xxxxx.
Store in MONO_SECRET_KEY env var.
Base URL:
<!-- TODO: confirm with https://docs.mono.co/docs/financial-data/webhook-introduction — if Mono has added HMAC signing for webhooks in a newer version, update accordingly -->https://api.withmono.com/v2Webhook Secret: Verify incoming webhooks using mono-webhook-secret header with HMAC-SHA512.
Before you can pull data, the user needs to link their bank account through Mono Connect (a drop-in widget). Here's how it works:
Client-side — embed the Mono Connect widget in your app:
<script src="https://connect.mono.co/connect.js"></script>
<script>
const connect = new Connect({
key: "live_pk_xxxxx", // Your PUBLIC key (not secret)
onSuccess: function(response) {
// response.code is a temporary auth code (expires in minutes)
// Send it to your server to exchange for an account ID
fetch('/api/mono/exchange', {
method: 'POST',
body: JSON.stringify({ code: response.code })
});
},
onClose: function() {
console.log('Widget closed');
}
});
connect.open();
</script>
Server-side — exchange the temporary code for a permanent account ID:
POST /accounts/auth
mono-sec-key: {secret_key}
{
"code": "code_xxxxx"
}
Response:
{
"status": "successful",
"message": "Account successfully linked",
"data": {
"id": "60d2a8a0xxxxx"
}
}
Save data.id — this is the account ID you'll use for all subsequent data requests.
GET /accounts/{account_id}
mono-sec-key: {secret_key}
Response:
{
"status": "successful",
"data": {
"account": {
"_id": "60d2a8a0xxxxx",
"name": "Amina Okafor",
"account_number": "0123456789",
"currency": "NGN",
"balance": 1250000,
"type": "SAVINGS_ACCOUNT",
"bvn": "22xxxxx789",
"institution": {
"name": "GTBank",
"bank_code": "058",
"type": "PERSONAL_BANKING"
}
}
}
}
Note: balance is in the minor unit (kobo for NGN). 1,250,000 kobo = ₦12,500.
GET /accounts/{account_id}/transactions?start=2025-01-01&end=2025-01-31&type=debit&paginate=true&limit=50&page=1
mono-sec-key: {secret_key}
Query params:
start, end — Date range (YYYY-MM-DD)type — debit, credit, or omit for bothnarration — Search by transaction descriptionpaginate — true for paginated resultslimit — Items per page (max 100)page — Page numberResponse:
{
"status": "successful",
"data": [
{
"_id": "txn_xxxxx",
"type": "debit",
"amount": 150000,
"narration": "POS Purchase - ShopRite Ikeja",
"date": "2025-01-15T14:30:00.000Z",
"balance": 1100000,
"currency": "NGN",
"category": "groceries"
}
],
"meta": {
"total": 245,
"page": 1,
"previous": null,
"next": "https://api.withmono.com/v2/accounts/xxx/transactions?page=2"
}
}
Pull verified income data — useful for lending decisions and credit scoring:
GET /accounts/{account_id}/income
mono-sec-key: {secret_key}
Response:
{
"status": "successful",
"data": {
"type": "SALARY",
"amount": 850000,
"employer": "Tech Company Ltd",
"confidence": 0.95,
"total_income": 10200000,
"annual_income": 10200000,
"monthly_income": 850000,
"income_streams": [
{
"type": "SALARY",
"amount": 850000,
"frequency": "MONTHLY",
"last_date": "2025-01-28",
"average_amount": 840000,
"number_of_occurrences": 12
}
]
}
}
Note: Income analysis may take time after account linking. You'll receive a mono.events.account_income webhook when analysis completes.
Retrieve KYC data from the user's bank:
GET /accounts/{account_id}/identity
mono-sec-key: {secret_key}
Response:
{
"status": "successful",
"data": {
"fullName": "Amina Chinyere Okafor",
"email": "[email protected]",
"phone": "+2348012345678",
"gender": "Female",
"dob": "1990-05-15",
"bvn": "22xxxxx789",
"maritalStatus": "Single",
"addressLine1": "123 Allen Avenue, Ikeja",
"state": "Lagos",
"city": "Lagos"
}
}
Categorized breakdown of the user's spending patterns:
GET /accounts/{account_id}/spending?period=last_6_months
mono-sec-key: {secret_key}
Response:
{
"status": "successful",
"data": {
"total_spent": 4500000,
"categories": [
{ "category": "food_and_dining", "amount": 1200000, "percentage": 26.7 },
{ "category": "transport", "amount": 800000, "percentage": 17.8 },
{ "category": "utilities", "amount": 600000, "percentage": 13.3 },
{ "category": "entertainment", "amount": 400000, "percentage": 8.9 },
{ "category": "transfers", "amount": 1500000, "percentage": 33.3 }
]
}
}
Verify identity information without requiring account linking. Endpoints include:
These enable KYC/KYB verification with minimal friction. Contact your Mono account manager to enable Lookup API access.
Initiate one-time direct debits from connected accounts. Useful for payments, subscriptions, and checkout:
POST /directpay/initiate
mono-sec-key: {secret_key}
{
"account_id": "60d2a8a0xxxxx",
"amount": 50000,
"type": "ONE_TIME",
"reference": "TXN_123456",
"description": "Payment for services"
}
Response:
{
"status": "successful",
"data": {
"id": "payment_xxxxx",
"reference": "TXN_123456",
"status": "PENDING"
}
}
DirectPay availability depends on account eligibility and bank support. Verify payment status via webhooks or the Verify endpoint.
When a user wants to disconnect their bank account:
POST /accounts/{account_id}/unlink
mono-sec-key: {secret_key}
Response:
{
"status": "successful",
"message": "Account unlinked successfully"
}
Mono sends webhooks for account events. Verify with your webhook secret:
Mono sends a static mono-webhook-secret header value that you compare directly against your configured webhook secret — it is not a computed HMAC. Verify as follows:
const incomingSecret = req.headers['mono-webhook-secret'];
if (incomingSecret !== process.env.MONO_WEBHOOK_SECRET) {
return res.status(401).end();
}
Financial Data Events:
mono.events.account_connected — User successfully linked an accountmono.events.account_updated — New data available (transactions, balance, etc.). Includes meta.data_status field: available, processing, or failedmono.events.account_income — Income analysis complete (sent after account linking and analysis finishes)mono.events.account_reauthorized — User re-authenticated (MFA accounts require reauth for fresh data)mono.events.account_unlinked — Account disconnectedPayment Events (DirectPay):
direct_debit.payment_successful — Payment completed successfullydirect_debit.payment_failed — Payment failedEach webhook payload includes account_id, timestamp, and event-specific data.
Important: Accounts with Multi-Factor Authentication (MFA) require periodic reauthorization before fresh data can be retrieved.
When MFA reauth is needed:
mono.events.account_reauthorized webhook firesRate Limiting: One Data Sync call per account every 2 minutes. Plan data refresh schedules accordingly.
All errors follow this format:
{
"status": "failed",
"message": "Description of what went wrong"
}
| Code | Error | Cause | Solution |
|---|---|---|---|
| 400 | Bad Request | Invalid params, malformed request, bad date format | Validate all parameters; use YYYY-MM-DD for dates |
| 401 | Unauthorized | Invalid, expired, or missing mono-sec-key | Verify secret key in environment; check sandbox vs. production |
| 404 | Not Found | Account ID invalid or account unlinked | Verify account ID exists; check if user unlinked account |
| 429 | Too Many Requests | Rate limit exceeded | Implement exponential backoff; respect Data Sync limits (1 call/2 min per account) |
| 503 | Service Unavailable | Bank API temporarily down or Mono infrastructure issue | Retry with exponential backoff; check Mono status page |
mono.events.account_reauthorized webhooks and update your user experience accordingly.processing briefly, then transitions to available. Poll or wait for the account_updated webhook before calling data endpoints.mono.events.account_income webhook before serving income data to users.code, not direct data.test_sk_; production keys start with live_sk_.mono-webhook-secret header and HMAC-SHA512./v2 endpoints (newer and more stable than /v1).GET /accounts/{id}/transactions to pull transaction historyGET /accounts/{id}/spending for categorized insightsaccount_updated to sync new transactionsaccount_reauthorized events for MFA accountsGET /accounts/{id}/income — verify and analyze incomeGET /accounts/{id}/transactions — analyze cash flow patterns (6-12 months)GET /accounts/{id}/identity — verify KYC dataGET /accounts/{id}/transactionsdirect_debit.payment_successful webhook