API Reference
A comprehensive REST API for lenders integrating their own core banking or loan management system with CreditPulse's Collections and Recovery platform.
Every endpoint returns the same envelope: { success, message, data }. Protected endpoints expect Authorization: Bearer <jwt>.
Getting started
1. Register an organization
/api/auth/register-organizationcurl -X POST http://localhost:8080/api/auth/register-organization \
-H "Content-Type: application/json" \
-d '{
"organizationName": "Precision Finance",
"industryType": "MICROFINANCE_BANK",
"ownerFullName": "Ada Okafor",
"ownerEmail": "ada@precisionfinance.ng",
"password": "StrongPass123!"
}'2. Create a customer
/api/customerscurl -X POST http://localhost:8080/api/customers \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
-d '{
"fullName": "Chinedu Eze",
"phoneNumber": "+2348012345678",
"employerName": "Lagos Logistics Ltd",
"employmentType": "SALARIED"
}'3. Import loans
/api/loans/bulk-syncexternalLoanId is your own loan reference — sending the same one again upserts the loan instead of duplicating it, which is what makes this safe to call from a nightly sync job.
curl -X POST http://localhost:8080/api/loans/bulk-sync \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
-d '[{
"externalLoanId": "LN-2026-0001",
"lenderProfileId": "33333333-...",
"customerFullName": "Chinedu Eze",
"principalNaira": 250000,
"outstandingBalanceNaira": 180000,
"disbursedAt": "2026-07-01T09:00:00Z",
"instalments": [
{ "instalmentNumber": 1, "dueDate": "2026-08-25", "amountDueNaira": 60000 }
]
}]'4. Initiate a debit mandate
/api/mandatesUse an Idempotency-Key header to safely retry mandate creation.
curl -X POST http://localhost:8080/api/mandates \
-H "Authorization: Bearer <jwt>" \
-H "Idempotency-Key: mandate-init-001" \
-H "Content-Type: application/json" \
-d '{
"customerId": "44444444-...",
"loanId": "55555555-...",
"bankCode": "058",
"accountNumber": "0123456789",
"maxAmountNaira": 60000,
"frequency": "MONTHLY",
"startDate": "2026-08-25",
"endDate": "2027-01-25"
}'5. Escalate to recovery
/api/recovery/cases/escalatecurl -X POST http://localhost:8080/api/recovery/cases/escalate \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
-d '{
"loanId": "55555555-...",
"reason": "Customer missed debit window twice and needs assisted recovery."
}'Data model at a glance
Organization (your tenant)
└─ LenderProfile the regulated lender who is the legal creditor of record
└─ Portfolio a named grouping of loans
└─ Customer a borrower
└─ Loan principal, outstanding balance, disbursement date
├─ Instalment one due payment on a loan's schedule
│ └─ DebitAttempt one attempt to collect an instalment
└─ Mandate the standing consent to debit a bank account
└─ RecoveryCase opened when a loan needs assisted recoveryLenderProfile, not Organization, is the creditor of record — CreditPulse never holds, pools, or settles customer money. Every tenant-scoped record carries a tenant_id enforced from your JWT, never a request field.
Status lifecycles
Mandate.statusPENDING → AWAITING_ACTIVATION → ACTIVE → (EXPIRED | REVOKED), or FAILED at any point before ACTIVE.
Only an ACTIVE mandate can be charged.
Instalment.statusPENDING → (PARTIALLY_PAID →) PAID, or OVERDUE once the due date passes unpaid, or WAIVED if written off.
DebitAttempt.statusPENDING → PROCESSING → SUCCESSFUL | FAILED | REVERSED | REFUNDED.
Each attempt is immutable once resolved — a retry creates a new row.
RecoveryCase.statusOPEN → IN_PROGRESS → (PTP_PENDING →) RESOLVED | ESCALATED | CLOSED.
Auth & access control
Every endpoint besides the ones below requires a bearer token, and most also require a specific permission — CreditPulse uses role-based access control, not "any authenticated user can call anything."
Public endpoints (no token required)
- POST /api/auth/register-organization
- POST /api/auth/login
- POST /api/auth/refresh
- POST /api/auth/verify-email?token=...
- POST /api/demo-requests
- GET /api/public/mandates/{id}/status
- GET /api/docs, GET /api/docs/markdown
Permission catalog
Assign these to roles via POST /api/roles. If your integration calls the API as a background job, create a dedicated service user with only the permissions its path needs, rather than reusing an owner/admin token.
/api/auth/register-organizationCreates a tenant, owner account, and initial JWT pair./api/auth/loginSigns a user in./api/auth/refreshExchanges a refresh token for a new token pair./api/auth/verify-email?token=...Verifies the emailed token./api/usersLists users for the current tenant./api/usersCreates a user./api/users/{id}/rolesChanges a user's role assignments./api/users/{id}/activeActivates or deactivates a user./api/rolesLists roles./api/roles/permissionsLists available permissions./api/rolesCreates a custom role./api/roles/{id}Updates a role./api/roles/{id}Deletes a role.Customers
/api/customersCreates a borrower/customer profile./api/customers/{id}Fetches one customer./api/customers?page=0&size=20Lists customers with pagination./api/customers/{customerId}/salary-observationsRecords salary observations used for prediction./api/customers/{customerId}/salary-prediction/latestReturns latest salary prediction./api/customers/{customerId}/salary-prediction/recomputeForces a prediction refresh./api/customers/{customerId}/salary-observationsLists recorded salary observations.Loans & portfolios
/api/loans/bulk-syncImports or upserts loans from JSON rows./api/loans/import-csvImports loans from a CSV file upload./api/loans/{id}Fetches one loan./api/loansLists loans with pagination./api/portfoliosCreates a portfolio./api/portfoliosLists portfolios./api/lender-profilesCreates a lender profile./api/lender-profilesLists lender profiles./api/lender-profiles/{id}/activeEnables or disables a lender profile.Mandates & collections
/api/mandatesInitiates a direct debit mandate./api/mandates/{id}/activateActivates an initiated mandate./api/mandates/{id}/revokeRevokes a mandate./api/mandates/banksLists supported banks./api/mandates/resolve-account?bankCode=058&accountNumber=0123456789Resolves account details before mandate setup./api/mandates/{id}Fetches one mandate./api/mandatesLists mandates./api/public/mandates/{id}/statusPublic status check for customer self-service./api/mandates/{mandateId}/account-statementUploads an account statement for name validation./api/mandates/{mandateId}/account-statementRetrieves statement metadata./api/collections/dashboardSummary metrics for collections./api/collections/queueQueue of instalments ready for collection work./api/collections/debit-calendarScheduled debit calendar./api/collections/cases/{instalmentId}Detailed collection case view./api/instalments/{instalmentId}/notice-sentMarks a debit notice as sent./api/instalments/{instalmentId}/debit-attemptsLogs a debit attempt./api/instalments/{instalmentId}/debit-attemptsLists debit attempts for one instalment./api/debit-attempts/{id}Fetches one debit attempt./api/debit-attemptsLists debit attempts using filters/pagination./api/collection-policiesCreates a collection policy./api/collection-policies/{id}/activeActivates or deactivates a collection policy./api/collection-policiesLists collection policies.Recovery
/api/recovery/dashboardHigh-level recovery KPIs./api/recovery/cases/escalateMoves a loan into recovery./api/recovery/cases/queueLists active recovery cases./api/recovery/cases/{id}Returns the basic recovery case view./api/recovery/cases/{id}/detailReturns case detail for the workspace UI./api/recovery/cases/{id}/assign?agentId={uuid}Assigns an agent./api/recovery/cases/{id}/status?status=IN_PROGRESSUpdates case status./api/recovery/cases/{id}/contactsLogs a contact event./api/recovery/cases/{id}/contactsReturns contact history./api/recovery/cases/{id}/eligibilityEvaluates channel/contact eligibility./api/recovery/cases/{caseId}/messagesSends or records a message./api/recovery/cases/{caseId}/messagesLists case messages./api/recovery/cases/{caseId}/callsCreates a call record./api/recovery/cases/{caseId}/callsLists calls for a case./api/calls/{id}Fetches one call./api/recovery/cases/{caseId}/promises-to-payCreates a promise to pay./api/recovery/cases/{caseId}/promises-to-payLists case promises to pay./api/promises-to-payLists promises to pay./api/promises-to-pay/{id}/keepMarks a promise kept./api/promises-to-pay/{id}/breakMarks a promise broken./api/promises-to-pay/{id}/cancelCancels a promise./api/disputesRaises a dispute./api/disputes/{id}/resolveResolves a dispute./api/disputes/{id}/rejectRejects a dispute./api/disputesLists disputes./api/hardship-casesOpens a hardship case./api/hardship-cases/{id}/approveApproves a hardship request./api/hardship-cases/{id}/denyDenies a hardship request./api/hardship-cases/{id}/resolveResolves the hardship case./api/hardship-casesLists hardship cases./api/recovery-workflowsCreates a workflow./api/recovery-workflows/{id}Updates a workflow./api/recovery-workflows/{id}/activeActivates or deactivates a workflow./api/recovery-workflowsLists workflows./api/recovery-workflows/{id}Fetches one workflow./api/contact-policiesCreates a contact policy./api/contact-policies/{id}/activeActivates or deactivates a contact policy./api/contact-policiesLists contact policies./api/message-templatesCreates a message template./api/message-templatesLists templates./api/message-templates/{id}/activeActivates or deactivates a template.Rules & reports
/api/rulesCreates a rule group/version./api/rules/groups/{ruleGroupId}/versionsCreates a new version for a rule group./api/rules/{id}/simulateRuns rule simulation./api/rules/{id}/approveApproves a draft rule./api/rules/{id}/publishPublishes a rule./api/rules/{id}/retireRetires a rule./api/rules/{id}/conflictsReturns rule conflicts./api/rulesLists rules./api/rules/groups/{ruleGroupId}/versionsLists rule versions for a group./api/rules/{id}Fetches one rule./api/reports/portfolioPortfolio report snapshot./api/reports/collections-performance?from=2026-08-01&to=2026-08-31Collections performance report./api/reports/agent-performanceAgent performance report./api/audit-eventsAudit trail listing./api/organizationCurrent tenant organization record./api/organizationUpdates organization settings.Wallet & billing
Every SMS, WhatsApp message, and voice-call minute your recovery workflows send is metered against a prepaid wallet balance, at the platform's published per-unit cost. Most integrators only need the balance and top-up routes.
/api/walletCurrent wallet balance./api/wallet/transactionsPaginated wallet debit/credit history./api/wallet/topupStarts a top-up (returns a payment authorization URL)./api/wallet/topup/{id}/confirmConfirms a top-up after payment completes.Real-time updates
CreditPulse currently receives webhooks; it does not yet send them. It has inbound webhook routes from its own payment and communications providers — that's how CreditPulse finds out a mandate activated or a call ended — but those exist between CreditPulse and its vendors, not between CreditPulse and you. There is no outbound mechanism today for CreditPulse to push an event (mandate activated, debit succeeded, case resolved) to your system the moment it happens.
Until that exists, poll the relevant GET endpoint on an interval matched to how time-sensitive that state is to you. If real-time push is a hard requirement, raise it before go-live — it changes what needs to be built, not just how you poll.
Errors & status codes
| Status | When it happens | data shape |
|---|---|---|
| 400 | Request body failed field validation | One entry per invalid field |
| 400 | Business-rule violation (invalid state for the action) | null, reason in message |
| 401 | Missing, expired, or invalid credentials | null |
| 403 | Authenticated but missing the required permission | null |
| 404 | Resource doesn't exist in your tenant | null |
| 409 | Conflicts with existing state (e.g. duplicate externalLoanId) | null |
| 500 | Unexpected server-side failure — treat as retryable | null |