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

# Repayments

> Record repayments and look up posted transactions against active loans.

A **repayment** is a transaction posted against an active loan. The API
lets you post a repayment, list a loan's repayments, and read a single
transaction back by id.

All repayment endpoints are nested under a loan. The loan can be
addressed two equivalent ways. **Prefer the externalId form** — it
lets you drive repayment flows off the reference you already store,
with no client-side mapping to a numeric LMS id.

### Addressing the loan

| Form                             | Path prefix                                            | When to use                                       |
| -------------------------------- | ------------------------------------------------------ | ------------------------------------------------- |
| By loan externalId (recommended) | `/v1/loans/external/{loan_external_id}/repayments/...` | Use the `externalId` you supplied on loan create. |
| By LMS id                        | `/v1/loans/{loan_id}/repayments/...`                   | Use the numeric `id` from the create response.    |

Both forms hit the same handlers and return identical responses. The
externalId form returns `404 not_found` if no loan matches.

```bash theme={null}
# By loan externalId (recommended)
curl -X POST "$BASE/v1/loans/external/loan-ext-12345/repayments" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"transactionAmount": 450}'

# Same effect, by LMS id
curl -X POST "$BASE/v1/loans/501/repayments" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"transactionAmount": 450}'
```

## The repayment object

The same object is returned by [repay](/repayments/repay),
[get a transaction](/repayments/get), the
[transaction list](/repayments/list), and as each entry of
`repaymentHistory` on the [loan object](/loans/overview).

<ResponseField name="id" type="string">
  Unique transaction identifier. Use it on [get](/repayments/get).
</ResponseField>

<ResponseField name="type" type="string">
  Transaction type label — always `Repayment`. The history is restricted
  to customer repayments; disbursements, waivers, and accruals are
  excluded.
</ResponseField>

<ResponseField name="date" type="string">
  ISO-8601 `YYYY-MM-DD`. Value date the transaction was effective.
</ResponseField>

<ResponseField name="amount" type="number">
  Total transaction amount.
</ResponseField>

<ResponseField name="principal" type="number">
  Portion applied to principal. Omitted if zero.
</ResponseField>

<ResponseField name="interest" type="number">
  Portion applied to interest. Omitted if zero.
</ResponseField>

<ResponseField name="fees" type="number">
  Portion applied to fees. Omitted if zero.
</ResponseField>

<ResponseField name="penalty" type="number">
  Portion applied to penalty charges. Omitted if zero.
</ResponseField>

<ResponseField name="overpayment" type="number">
  Overpayment amount, present only when the transaction exceeded the
  outstanding balance. A strong signal that something needs reconciling.
</ResponseField>

<ResponseField name="status" type="string">
  `posted` for a normal transaction, or `reversed` if it has been
  reversed (an operational LMS-side action).
</ResponseField>

## Endpoints

| Method | Path                                                                | Purpose                                                     |
| ------ | ------------------------------------------------------------------- | ----------------------------------------------------------- |
| POST   | `/v1/loans/external/{loan_external_id}/repayments`                  | [Repay](/repayments/repay) by loan externalId (recommended) |
| POST   | `/v1/loans/{loan_id}/repayments`                                    | [Repay](/repayments/repay) by LMS id                        |
| GET    | `/v1/loans/external/{loan_external_id}/repayments`                  | [List repayments](/repayments/list)                         |
| GET    | `/v1/loans/external/{loan_external_id}/repayments/{transaction_id}` | [Get](/repayments/get) a transaction                        |

<Note>
  The repayment **reads** (list and get-one) are available in the
  externalId form only. Posting a repayment still supports both the
  externalId and numeric-id forms.
</Note>

## Repayment history: two ways to read it

The repayment history is available in two places, both returning the
object above and both containing **only customer repayments** (newest
first) — disbursements, waivers, and accruals are excluded:

* **Embedded snapshot** — every single-loan read
  (`GET /v1/loans/external/{loan_external_id}`) includes a
  `repaymentHistory` array. Use this for a quick view without a second
  call. It is **not** included on the loan *list* endpoint, only on
  single-loan reads.
* **Paginated list** — `GET /v1/loans/external/{loan_external_id}/repayments`
  ([reference](/repayments/list)) returns the same entries with
  `page`/`rows` pagination, for loans with many partial payments.

## Request body

`POST /v1/loans/.../repayments` takes:

<ParamField body="transactionAmount" type="number" required>
  Must be greater than 0.
</ParamField>

<ParamField body="transactionDate" type="string">
  Optional value date (ISO `yyyy-MM-dd`). Defaults to today. See
  [Reconciling delayed settlements](#reconciling-delayed-settlements).
</ParamField>

<ParamField body="idempotencyKey" type="string">
  Optional dedupe token — typically your wallet transaction reference
  (max 100 characters). A repayment carrying a key already seen on the
  loan returns the original transaction unchanged, so retries never
  double-post. Strongly recommended on every repayment.
</ParamField>

The payment method is filled in by the service.

## Reconciling delayed settlements

A common integration pattern is to debit the customer's wallet first,
then call this API to settle the loan. If that settle call fails, times
out, or is only retried the next day, the payment still happened on your
side — and your records are the source of truth for *when*.

Pass `transactionDate` set to the date the wallet was actually debited.
The repayment is then booked **as of that value date** rather than the
day the API call lands. What that does:

* **It records when the payment happened.** Allocation and balances
  reflect the backdated date.
* **On-time payments are not penalised for your delay.** If the value
  date is on/before the due date and the LMS had already raised a late
  penalty while your call was in flight, the service automatically
  cancels that penalty (waives and deactivates it) **before** posting the
  repayment, then lets the overdue-penalty job recompute it against
  whatever is genuinely still overdue:
  * **Paid in full on time** → nothing remains overdue → no penalty at
    all (net zero).
  * **Partial on-time payment** → the penalty is recomputed on the
    remaining overdue balance only. A 50,000 payment against a 100,000
    principal leaves 61,000 overdue, so the penalty becomes 10% of
    61,000 — not the original 100,000.
* **Late payments still bear the penalty.** If the value date is *after*
  the due date, the payment is genuinely late and the penalty stands
  (paid penalty → interest → principal).

<Note>
  The recomputed penalty appears at the **next run of the overdue-penalty
  job** — the same nightly cycle that raises every penalty. So
  immediately after an on-time partial payment the loan shows no penalty
  and a reduced balance; the corrected penalty lands on the next cycle,
  exactly as it would have with no delay.
</Note>

```bash theme={null}
# Wallet debited 2026-06-01; settle call only succeeds on 2026-06-02.
# Book it as of 2026-06-01 so the delay isn't counted against the customer.
# Include the wallet reference so the call is safe to retry.
curl -X POST "$BASE/v1/loans/external/loan-ext-12345/repayments" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"transactionAmount": 11100, "transactionDate": "2026-06-01", "idempotencyKey": "wallet-txn-abc123"}'
```

<Note>
  The loan products use **flat interest** fixed at disbursement, so
  the value date never changes the *interest* owed. The value date must
  be on/after disbursement and not in the future.
</Note>
