FOR ENGINEERS

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

POST/api/auth/register-organization
cURL
curl -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

POST/api/customers
cURL
curl -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

POST/api/loans/bulk-sync

externalLoanId 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
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

POST/api/mandates

Use an Idempotency-Key header to safely retry mandate creation.

cURL
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

POST/api/recovery/cases/escalate
cURL
curl -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 recovery

LenderProfile, 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.status

PENDING → AWAITING_ACTIVATION → ACTIVE → (EXPIRED | REVOKED), or FAILED at any point before ACTIVE.

Only an ACTIVE mandate can be charged.

Instalment.status

PENDING → (PARTIALLY_PAID →) PAID, or OVERDUE once the due date passes unpaid, or WAIVED if written off.

DebitAttempt.status

PENDING → PROCESSING → SUCCESSFUL | FAILED | REVERSED | REFUNDED.

Each attempt is immutable once resolved — a retry creates a new row.

RecoveryCase.status

OPEN → 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.

ORG_MANAGELENDERS_MANAGEPORTFOLIOS_MANAGEUSERS_MANAGEROLES_MANAGELOANS_MANAGECUSTOMERS_MANAGEMANDATES_MANAGESALARY_PREDICTIONS_MANAGECOLLECTION_POLICY_MANAGEDEBITS_ATTEMPTRECOVERY_CASES_VIEWRECOVERY_CASES_MANAGECALLS_PLACEMESSAGES_SENDMESSAGE_TEMPLATES_MANAGEPROMISES_TO_PAY_MANAGEDISPUTES_MANAGEHARDSHIP_MANAGECONTACT_POLICY_MANAGERECOVERY_WORKFLOWS_MANAGERULES_MANAGERULES_APPROVERULES_PUBLISHAUDIT_LOG_VIEWWALLET_MANAGE
POST
/api/auth/register-organizationCreates a tenant, owner account, and initial JWT pair.
POST
/api/auth/loginSigns a user in.
POST
/api/auth/refreshExchanges a refresh token for a new token pair.
POST
/api/auth/verify-email?token=...Verifies the emailed token.
GET
/api/usersLists users for the current tenant.
POST
/api/usersCreates a user.
PUT
/api/users/{id}/rolesChanges a user's role assignments.
PUT
/api/users/{id}/activeActivates or deactivates a user.
GET
/api/rolesLists roles.
GET
/api/roles/permissionsLists available permissions.
POST
/api/rolesCreates a custom role.
PUT
/api/roles/{id}Updates a role.
DELETE
/api/roles/{id}Deletes a role.

Customers

POST
/api/customersCreates a borrower/customer profile.
GET
/api/customers/{id}Fetches one customer.
GET
/api/customers?page=0&size=20Lists customers with pagination.
POST
/api/customers/{customerId}/salary-observationsRecords salary observations used for prediction.
GET
/api/customers/{customerId}/salary-prediction/latestReturns latest salary prediction.
POST
/api/customers/{customerId}/salary-prediction/recomputeForces a prediction refresh.
GET
/api/customers/{customerId}/salary-observationsLists recorded salary observations.

Loans & portfolios

POST
/api/loans/bulk-syncImports or upserts loans from JSON rows.
POST
/api/loans/import-csvImports loans from a CSV file upload.
GET
/api/loans/{id}Fetches one loan.
GET
/api/loansLists loans with pagination.
POST
/api/portfoliosCreates a portfolio.
GET
/api/portfoliosLists portfolios.
POST
/api/lender-profilesCreates a lender profile.
GET
/api/lender-profilesLists lender profiles.
PUT
/api/lender-profiles/{id}/activeEnables or disables a lender profile.

Mandates & collections

POST
/api/mandatesInitiates a direct debit mandate.
POST
/api/mandates/{id}/activateActivates an initiated mandate.
POST
/api/mandates/{id}/revokeRevokes a mandate.
GET
/api/mandates/banksLists supported banks.
GET
/api/mandates/resolve-account?bankCode=058&accountNumber=0123456789Resolves account details before mandate setup.
GET
/api/mandates/{id}Fetches one mandate.
GET
/api/mandatesLists mandates.
GET
/api/public/mandates/{id}/statusPublic status check for customer self-service.
POST
/api/mandates/{mandateId}/account-statementUploads an account statement for name validation.
GET
/api/mandates/{mandateId}/account-statementRetrieves statement metadata.
GET
/api/collections/dashboardSummary metrics for collections.
GET
/api/collections/queueQueue of instalments ready for collection work.
GET
/api/collections/debit-calendarScheduled debit calendar.
GET
/api/collections/cases/{instalmentId}Detailed collection case view.
POST
/api/instalments/{instalmentId}/notice-sentMarks a debit notice as sent.
POST
/api/instalments/{instalmentId}/debit-attemptsLogs a debit attempt.
GET
/api/instalments/{instalmentId}/debit-attemptsLists debit attempts for one instalment.
GET
/api/debit-attempts/{id}Fetches one debit attempt.
GET
/api/debit-attemptsLists debit attempts using filters/pagination.
POST
/api/collection-policiesCreates a collection policy.
PUT
/api/collection-policies/{id}/activeActivates or deactivates a collection policy.
GET
/api/collection-policiesLists collection policies.

Recovery

GET
/api/recovery/dashboardHigh-level recovery KPIs.
POST
/api/recovery/cases/escalateMoves a loan into recovery.
GET
/api/recovery/cases/queueLists active recovery cases.
GET
/api/recovery/cases/{id}Returns the basic recovery case view.
GET
/api/recovery/cases/{id}/detailReturns case detail for the workspace UI.
PUT
/api/recovery/cases/{id}/assign?agentId={uuid}Assigns an agent.
PUT
/api/recovery/cases/{id}/status?status=IN_PROGRESSUpdates case status.
POST
/api/recovery/cases/{id}/contactsLogs a contact event.
GET
/api/recovery/cases/{id}/contactsReturns contact history.
GET
/api/recovery/cases/{id}/eligibilityEvaluates channel/contact eligibility.
POST
/api/recovery/cases/{caseId}/messagesSends or records a message.
GET
/api/recovery/cases/{caseId}/messagesLists case messages.
POST
/api/recovery/cases/{caseId}/callsCreates a call record.
GET
/api/recovery/cases/{caseId}/callsLists calls for a case.
GET
/api/calls/{id}Fetches one call.
POST
/api/recovery/cases/{caseId}/promises-to-payCreates a promise to pay.
GET
/api/recovery/cases/{caseId}/promises-to-payLists case promises to pay.
GET
/api/promises-to-payLists promises to pay.
POST
/api/promises-to-pay/{id}/keepMarks a promise kept.
POST
/api/promises-to-pay/{id}/breakMarks a promise broken.
POST
/api/promises-to-pay/{id}/cancelCancels a promise.
POST
/api/disputesRaises a dispute.
POST
/api/disputes/{id}/resolveResolves a dispute.
POST
/api/disputes/{id}/rejectRejects a dispute.
GET
/api/disputesLists disputes.
POST
/api/hardship-casesOpens a hardship case.
POST
/api/hardship-cases/{id}/approveApproves a hardship request.
POST
/api/hardship-cases/{id}/denyDenies a hardship request.
POST
/api/hardship-cases/{id}/resolveResolves the hardship case.
GET
/api/hardship-casesLists hardship cases.
POST
/api/recovery-workflowsCreates a workflow.
PUT
/api/recovery-workflows/{id}Updates a workflow.
PUT
/api/recovery-workflows/{id}/activeActivates or deactivates a workflow.
GET
/api/recovery-workflowsLists workflows.
GET
/api/recovery-workflows/{id}Fetches one workflow.
POST
/api/contact-policiesCreates a contact policy.
PUT
/api/contact-policies/{id}/activeActivates or deactivates a contact policy.
GET
/api/contact-policiesLists contact policies.
POST
/api/message-templatesCreates a message template.
GET
/api/message-templatesLists templates.
PUT
/api/message-templates/{id}/activeActivates or deactivates a template.

Rules & reports

POST
/api/rulesCreates a rule group/version.
POST
/api/rules/groups/{ruleGroupId}/versionsCreates a new version for a rule group.
POST
/api/rules/{id}/simulateRuns rule simulation.
POST
/api/rules/{id}/approveApproves a draft rule.
POST
/api/rules/{id}/publishPublishes a rule.
POST
/api/rules/{id}/retireRetires a rule.
GET
/api/rules/{id}/conflictsReturns rule conflicts.
GET
/api/rulesLists rules.
GET
/api/rules/groups/{ruleGroupId}/versionsLists rule versions for a group.
GET
/api/rules/{id}Fetches one rule.
GET
/api/reports/portfolioPortfolio report snapshot.
GET
/api/reports/collections-performance?from=2026-08-01&to=2026-08-31Collections performance report.
GET
/api/reports/agent-performanceAgent performance report.
GET
/api/audit-eventsAudit trail listing.
GET
/api/organizationCurrent tenant organization record.
PATCH
/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.

GET
/api/walletCurrent wallet balance.
GET
/api/wallet/transactionsPaginated wallet debit/credit history.
POST
/api/wallet/topupStarts a top-up (returns a payment authorization URL).
POST
/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

StatusWhen it happensdata shape
400Request body failed field validationOne entry per invalid field
400Business-rule violation (invalid state for the action)null, reason in message
401Missing, expired, or invalid credentialsnull
403Authenticated but missing the required permissionnull
404Resource doesn't exist in your tenantnull
409Conflicts with existing state (e.g. duplicate externalLoanId)null
500Unexpected server-side failure — treat as retryablenull
Non-technical stakeholder on your team? Point them to the Integration Guide instead — same ground, no JSON.