IDs
Every resource ID is exposed as a string.- Treat IDs as opaque. Do not parse them as integers in your client code; future versions may switch to UUIDs.
- Pass them back exactly as received.
- Path params accept the same string format:
GET /v1/customers/42.
"productId": 1). Responses always come back as strings. One
deliberate exception: customerId on
POST /v1/loans carries the customer’s externalId
(a string), not the numeric LMS id — see below.
externalIds
Every customer and loan also carries a partner-suppliedexternalId —
a string you choose (account number, MSISDN, your own UUID, a wallet
transaction reference). The LMS enforces uniqueness across customers
on one externalId space and across loans on another.
externalIds are the primary integration key — drive your
integration off the identifiers you already store and skip the mapping
back to numeric LMS ids. Prefer the externalId form everywhere you can.
Where externalIds work
Two distinct namespaces
- A customer externalId identifies a person/account.
- A loan externalId identifies one specific loan.
customerExternalId for customer-scoped filters,
customer_external_id on customer resource paths,
loan_external_id on loan-scoped paths (single-loan reads and the
whole repayment family). A loan list filtered by
customerExternalId returns N loans; a single-loan path like
/loans/external/{loan_external_id} or
/loans/external/{loan_external_id}/repayments/... targets one
specific loan.
Limitations
PUT /v1/loans/{loan_id}(update) and thewriteoffendpoint are numeric-id only. The wallet-rollback hot path (undo-disbursal → undo-approval → delete) is fully externalId-addressable.
Errors
- Unknown externalId on a path lookup →
404 not_foundfrom the upstream LMS. - Unknown externalId on a query filter (
?externalId=,?customerExternalId=) → empty page (total: 0, items: []), preserving the list response shape.
Dates
All dates on the wire are ISO-8601YYYY-MM-DD strings.
Africa/Dar_es_Salaam, UTC+3) on the current deployments. A
repayment posted without a value date is booked on today’s date in
that zone, not in UTC.
Timestamps
A few fields are exact instants, not dates. They are RFC3339 strings with the tenant’s offset:disbursedAt/dueDateTimeon the loan object — the instant the loan was disbursed and the instant it falls due and its overdue penalty fires (disbursedAt+ the product term, to the second).transactionAton repayments — the instant the money moved on your side. Any RFC3339 offset is accepted (Zincluded); the service converts.
nextDueDate and repaymentSchedule[].dueDate
remain YYYY-MM-DD.

