REST API
The REST API at /api/v1/ is the recommended integration surface for third parties. It is OAuth-protected, OpenAPI-described, and built for predictable, idempotent operations.

At a glance — Base URL: https:///api/v1/. Auth: OAuth 2.0 client credentials. Content-Type: application/json. Pagination: cursor-based. Idempotency: via Idempotency-Key header on POSTs.
Design principles
- Resources named in singular BridgeERP model form (members, loans, savings_accounts)
- Identifiers are stable BridgeERP integer IDs; UUIDs available via aliasing if a consumer requires non-enumerable IDs
- Timestamps in ISO 8601 UTC with Z suffix
- Currency amounts as decimals in minor units (KES cents) to avoid float drift
- All list endpoints support filter[field]=value query params and cursor pagination
- All mutating endpoints accept Idempotency-Key for safe retry
Key endpoint groups
| Group | Endpoint | Methods |
|---|---|---|
| Members | /api/v1/members | GET, POST, PATCH |
| Loans | /api/v1/loans | GET, POST, PATCH |
| Loan schedules | /api/v1/loans/{id}/schedule | GET |
| Disbursements | /api/v1/loans/{id}/disbursements | GET, POST |
| Repayments | /api/v1/loans/{id}/repayments | GET, POST |
| Savings accounts | /api/v1/savings_accounts | GET, POST, PATCH |
| Savings transactions | /api/v1/savings_accounts/{id}/transactions | GET, POST |
| Mobile money | /api/v1/mobile_money/transactions | GET, POST |
| AML alerts | /api/v1/aml/alerts | GET, PATCH |
| Webhooks | /api/v1/webhooks | GET, POST, DELETE |
Example: create a member
curl -X POST https://microfinance.mybridgeerp.com/api/v1/members \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 4c2a-jane-wambui-2026-03-15' \
-d '{
"name": "Jane Wambui Mwangi",
"national_id": "22748561",
"phone": "+254712345678",
"branch_id": 3,
"kyc": {
"id_type": "national_id",
"id_number": "22748561",
"date_of_birth": "1988-07-12",
"address": {
"city": "Nairobi",
"county": "Nairobi",
"country": "KE"
}
}
}'
# 201 Created
# {
# "id": 4827,
# "member_no": "M-0004827",
# "name": "Jane Wambui Mwangi",
# "state": "pending_kyc",
# ...
# }
Example: create and disburse a loan
# 1. Create loan in draft
curl -X POST https://microfinance.mybridgeerp.com/api/v1/loans \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{
"member_id": 4827,
"product_id": 12,
"principal": 5000000,
"term_months": 12,
"purpose": "Working capital for tailoring shop"
}'
# {"id": 9183, "state": "draft", ...}
# 2. Approve
curl -X PATCH https://microfinance.mybridgeerp.com/api/v1/loans/9183 \
-H 'Authorization: Bearer <token>' \
-d '{"action": "approve"}'
# 3. Disburse to M-Pesa
curl -X POST https://microfinance.mybridgeerp.com/api/v1/loans/9183/disbursements \
-H 'Authorization: Bearer <token>' \
-H 'Idempotency-Key: disburse-9183-2026-03-15' \
-d '{
"amount": 5000000,
"channel": "mpesa_b2c",
"msisdn": "+254712345678"
}'
# {"id": 7211, "state": "sent", "provider_ref": "PBJ4S6X9DC"}
Error format
Errors follow RFC 7807 Problem Details for HTTP APIs.
{
"type": "https://docs.mybridgeerp.com/errors/insufficient_balance",
"title": "Insufficient agent float",
"status": 409,
"detail": "M-Pesa B2C float is KES 32,000 but disbursement requires KES 50,000.",
"instance": "/api/v1/loans/9183/disbursements",
"agent_id": "agent-nairobi-01",
"available_float": 3200000
}
Worked scenarios
Scenario — KCB Bank integrates loan disbursement via REST
| Character | Role |
|---|---|
| Samuel Kibet | Mwananchi CTO |
| Aisha Hassan | KCB Integration Lead |
| Peter Otieno | Mwananchi Operations |
Timeline
- Day 1, 10:00: Samuel provisions OAuth client for KCB. (Client id svc_kcb_disbursement, scopes loans:read disbursements:write.)
- Day 1, 14:00: Aisha integrates against /api/v1/loans?filter[state]=ready_to_disburse. (KCB polls every 5 minutes, pulls eligible loans.)
- Day 2, 09:00: Mwananchi approves 47 loans totalling KES 12.4M. (Loans state=ready_to_disburse.)
- Day 2, 09:05: KCB pulls the list, issues bulk EFT. (Bulk reference KCB-BULK-2026-03-16-001.)
- Day 2, 09:20: KCB POSTs disbursement confirmations. (47 POST /api/v1/loans/{id}/disbursements with KCB ref.)
- Day 2, 09:21: MFI Suite books disbursements, fires loan.disbursed webhooks. (Loan state=active, schedule starts.)
- Day 2, 09:30: Peter sees 47 active loans in dashboard. (All linked to KCB references for reconciliation.)
Outcome — Daily disbursement batch automated end-to-end. Reconciliation between MFI Suite ledger and KCB bank statement is exact via the KCB ref captured in mfi.loan.disbursement.provider_ref.
Reference
Common request and response headers
| Header | Direction | Purpose |
|---|---|---|
| Authorization: Bearer | Request | OAuth access token |
| Content-Type: application/json | Request | JSON body |
| Idempotency-Key | Request | Safe retry for mutating endpoints (24h TTL) |
| If-Match: | Request | Optimistic concurrency for PATCH |
| X-Request-ID | Request/Response | Echo trace ID for debugging |
| ETag | Response | Optimistic concurrency token |
| X-RateLimit-Remaining | Response | Requests remaining in window |
| Retry-After | Response (429) | Seconds to wait before retry |
| Deprecation, Sunset | Response | Endpoint deprecation signals |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| POST /loans returns 422 with 'product_id required'. | Forgot the loan product reference. | Provide product_id matching a row from GET /api/v1/loan_products. |
| Repayment returns 200 but loan balance unchanged. | Repayment booked to a different loan (wrong loan id in URL). | Always cross-check the response payload's loan_id; never reuse stale URLs from cached lists. |
| Idempotency-Key returns cached response from yesterday. | Key reused within 24h TTL window. | Use unique idempotency keys per logical operation; encode date or sequence. |
See also
Was this page helpful?

