> ## 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.

# List loans

> Fetch a paginated list of loans, filterable by customer and status.

Returns a page of loans. See [Pagination](/concepts/pagination) for the
common query parameter contract.

## Query parameters

<ParamField query="page" type="integer" default="1" />

<ParamField query="rows" type="integer" default="10" />

<ParamField query="orderBy" type="string">
  Common values: `id`, `principal`, `expectedDisbursementDate`.
</ParamField>

<ParamField query="sortOrder" type="string">
  `ASC` or `DESC`.
</ParamField>

<ParamField query="customerId" type="integer">
  Restrict the result to loans belonging to a specific customer (by
  internal LMS id). Mutually exclusive with `customerExternalId` —
  pass one or the other, not both.
</ParamField>

<ParamField query="customerExternalId" type="string">
  Restrict the result to loans belonging to the customer with this
  partner-supplied `externalId`. Mutually exclusive with `customerId`.
  Named explicitly because loans have their own `externalId` field too
  — this one filters by the **customer's** externalId, not the loan's.
</ParamField>

<ParamField query="status" type="string">
  Case-insensitive exact match against the loan's `status`. Accepted
  values are the exact strings the LMS returns: `Active`,
  `Submitted and pending approval`, `Approved`, `Withdrawn by applicant`,
  `Rejected`, `Closed (obligations met)`, `Closed (written off)`,
  `Overpaid`. The filter is applied wrapper-side after fetching, so
  `total` reflects the *filtered* count, not the unfiltered scope.
  Scans up to 1000 loans per call.
</ParamField>

## Examples

```bash theme={null}
# All loans for customer 42 (by LMS id)
curl -sf "$BASE/v1/loans?customerId=42&page=1&rows=25" \
  -H "Authorization: Bearer $TOKEN"

# Same customer, looked up by their externalId
curl -sf "$BASE/v1/loans?customerExternalId=ext-ada-001" \
  -H "Authorization: Bearer $TOKEN"

# Only this customer's currently-active loans
curl -sf "$BASE/v1/loans?customerExternalId=ext-ada-001&status=Active" \
  -H "Authorization: Bearer $TOKEN"
```

## Response

Each row has the **same shape as a single-loan read** — balance
roll-up, next-due summary, and the full `repaymentSchedule` — so
partners can render outstanding-balance / next-instalment / schedule
views without a per-loan detail call.

```json theme={null}
{
  "items": [
    {
      "id": "501",
      "accountNo": "000000501",
      "externalId": "loan-ext-12345",
      "customerId": "42",
      "customerName": "Ada Lovelace",
      "loanProductId": "1",
      "loanProductName": "7-Day Loan",
      "status": "Active",
      "principal": 5000,
      "approvedPrincipal": 5000,
      "currencyCode": "TZS",
      "termFrequency": 7,
      "numberOfRepayments": 1,
      "interestRatePerPeriod": 11,

      "principalDisbursed": 5000,
      "principalPaid": 0,
      "principalOutstanding": 5000,
      "interestCharged": 38.5,
      "interestPaid": 0,
      "interestOutstanding": 38.5,
      "feeCharged": 0,
      "feePaid": 0,
      "feeOutstanding": 0,
      "penaltyCharged": 0,
      "penaltyPaid": 0,
      "penaltyOutstanding": 0,
      "totalRepayment": 0,
      "totalOutstanding": 5038.5,
      "totalOverpaid": 0,

      "nextDueDate": "2026-06-04",
      "nextDueAmount": 5038.5,
      "overdue": false,

      "repaymentSchedule": [
        {
          "period": 1,
          "fromDate": "2026-05-28",
          "dueDate": "2026-06-04",
          "complete": false,
          "principalDue": 5000,
          "principalOutstanding": 5000,
          "interestDue": 38.5,
          "interestOutstanding": 38.5,
          "totalDue": 5038.5,
          "totalOutstanding": 5038.5
        }
      ]
    }
  ],
  "total": 7,
  "page": 1,
  "rowsPerPage": 25
}
```

See [the loan object](/loans/overview#the-loan-object) for the full
field reference.

<Note>
  `repaymentSchedule` on list rows is **best-effort**: each row is
  hydrated with a per-loan read behind the scenes, and if that read
  fails transiently the row is returned without the schedule rather
  than failing your whole page. `nextDueDate` / `nextDueAmount` are
  still present in that case. Treat a missing schedule on a list row
  as "retry or fetch the single loan", not as "this loan has no
  schedule" — `GET /v1/loans/{id}` always carries it.
</Note>
