> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lms.bsa.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# IDs, dates, and money

> How identifiers, dates, and amounts are represented on the wire.

## IDs

Every resource ID is exposed as a **string**.

```json theme={null}
{
  "id": "42",
  "customerId": "17",
  "officeId": "1"
}
```

**Rules:**

* 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`.

Note that some **write payloads** still accept integer IDs
(`"productId": 1`). Responses always come back as strings. One
deliberate exception: `customerId` on
[POST /v1/loans](/loans/create) carries the **customer's externalId**
(a string), not the numeric LMS id — see below.

## externalIds

Every customer and loan also carries a partner-supplied `externalId` —
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

| Surface                                   | Form  | Example                                                       |
| ----------------------------------------- | ----- | ------------------------------------------------------------- |
| Find a customer                           | query | `GET /v1/customers?externalId=X`                              |
| Get/update/delete/close a customer        | path  | `GET /v1/customers/external/{externalId}`                     |
| Create a loan (reference the customer)    | body  | `POST /v1/loans` with `"customerId": "<customer externalId>"` |
| Get one loan                              | path  | `GET /v1/loans/external/{loan_external_id}`                   |
| Filter loans for a customer               | query | `GET /v1/loans?customerExternalId=X`                          |
| Post a repayment (and every repayment op) | path  | `POST /v1/loans/external/{loan_external_id}/repayments`       |
| Credit scoring                            | path  | `GET /v1/credit-scorecard/{externalId}`                       |

### Two distinct namespaces

* A **customer externalId** identifies a person/account.
* A **loan externalId** identifies one specific loan.

The path segments make this explicit:
`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 the `writeoff` endpoint 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_found` from 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-8601 `YYYY-MM-DD`** strings.

```json theme={null}
{
  "activationDate": "2026-05-22",
  "submittedDate": "2026-05-20"
}
```

**Time zones:** dates have no timezone attached. They represent the
calendar date in the deployment's configured locale.

## Money

Amounts (principal, interest rate, transaction amount, credit limit)
are represented as **JSON numbers**:

```json theme={null}
{
  "principal": 5000,
  "interestRatePerPeriod": 1.5,
  "creditLimit": 12000.50
}
```
