> For the complete documentation index, see [llms.txt](https://developers.gallantreecapital.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.gallantreecapital.com/resources/loans.md).

# Loans

The **loan** is the resource most integrations spend most of their time on. This guide covers reading loans, creating them, the status lifecycle, and the fields you'll actually care about.

Before you start: read [Data model](/concepts/data-model.md). Loans reference capital programs, products, borrowers, and collaterals — you need the mental model of how those fit together.

## Read

### List loans

```bash
curl -sS -H "x-api-key: $KEY" \
  "https://developers.gallantreecapital.com/api/v1/loans?page=1&limit=50"
```

Paginated (see [API versioning → Pagination](/guides/versioning.md#pagination)). Returns loans that belong to the lender your key resolves to.

**Required scope:** `loans:read`.

### Get one loan

```bash
curl -sS -H "x-api-key: $KEY" \
  "https://developers.gallantreecapital.com/api/v1/loans/<loan-id>"
```

Returns a `V1LoanDto` — the fields listed below.

**Required scope:** `loans:read`.

## The response shape

```json
{
  "id": "65f3a8b1d5c9e4f2a1b3c4d5",
  "name": "Sandbox Loan #1",
  "category": "cre",
  "status": "current",
  "mainStatus": "active",

  "principalAmount": 1250000,
  "originalPrincipalAmount": 1500000,
  "currentPrincipalAmount": 1250000,
  "approvedPrincipalAmount": 1500000,

  "loanTerm": 60,
  "loanTermRemaining": 42,
  "loanMaturityDate": "2031-08-14T00:00:00.000Z",
  "expectedStartDate": "2026-08-14T00:00:00.000Z",
  "startDate": "2026-08-14T00:00:00.000Z",

  "loanRank": "senior",
  "loanExitStrategy": "refinance",
  "purposeOfLoan": "acquisition",

  "currency": "AUD",
  "description": null,

  "totalCollateralValue": 2500000,
  "originalLVR": 60,
  "currentLVR": 50,

  "isSyndicated": false,

  "capitalProgramId": "65f3...c4d0",
  "productId": "65f3...c4e1",
  "borrowingEntityId": "65f3...c4f2",
  "collateralIds": ["65f3...c503", "65f3...c504"],

  "createdAt": "2026-07-14T02:11:00.000Z",
  "updatedAt": "2026-08-02T09:42:11.000Z"
}
```

The full field list — including every optional field, its type, and its meaning — is on the [API reference](/reference/openapi.md).

Fields worth calling out:

| Field                                                                                      | Notes                                                                                                                                                                                                                                                                                                                     |
| ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`category`**                                                                             | The loan's high-level classification. Values: `cre` (commercial real estate), `cc` (corporate credit), `mmc` (middle-market corporate), `homeLoan` (AU residential), `fof` (fund-of-funds). Determines which optional fields are populated — e.g. `wale` / `dscr` / `icr` are CRE-shaped and won't appear on `mmc` loans. |
| **`status`**                                                                               | The detailed lifecycle status — see below.                                                                                                                                                                                                                                                                                |
| **`mainStatus`**                                                                           | A rolled-up view of `status` for coarse filtering (`draft`, `active`, `complete`, etc.).                                                                                                                                                                                                                                  |
| **`principalAmount`** vs three siblings                                                    | `originalPrincipalAmount` = at settlement; `currentPrincipalAmount` = outstanding today; `approvedPrincipalAmount` = credit-approved (may differ from settled).                                                                                                                                                           |
| **`loanTerm`** / **`loanTermRemaining`**                                                   | In months.                                                                                                                                                                                                                                                                                                                |
| **`capitalProgramId`** / **`productId`** / **`borrowingEntityId`** / **`collateralIds[]`** | Follow the pointers — see [Data model](/concepts/data-model.md).                                                                                                                                                                                                                                                          |

## Status lifecycle

Loans walk through 25+ statuses from `draft` all the way through to `complete` or `written_off`. The most common ones you'll see:

**Origination**

* `draft` → `discovery` → `verification_required` → `packaging` → `pre_credit_qa` → `pending_credit_approval` → `credit_approved`

**Settlement**

* `settlement_arranged` → `notes_approved` → `disbursement_approved` → `disbursement_settled`

**Servicing**

* `current` — performing
* `arrears3` / `arrears7` / `arrears14` / `arrears21` / `arrears30` / `arrears45` / `arrears60` / `arrears75` / `arrears90` — days-past-due tranches
* `default`

**Terminal**

* `complete` — repaid
* `written_off`
* `declined` — never funded

You cannot set `status` directly through the API — status transitions are driven by servicing events (payment received, arrears roll, credit committee decision). If you're subscribing to changes, listen to `loan.status_changed` on webhooks (see [Subscribe to events](/webhooks/subscribe.md)).

## Create

Loan creation uses the **handover payload** — a bundle that carries the loan **plus** all the counterparties it references (borrower, guarantors, brokers, servicers, etc.) in a single atomic request. This lets you create a fully-formed loan without pre-provisioning organisations.

```
POST /api/v1/loans
x-api-key:        <your key>
Idempotency-Key:  <UUID — see idempotency guide>
Content-Type:     application/json

{
  "source": "my-integration",              # optional label
  "organisations": [ … ],                   # required, min 1
  "collaterals":   [ … ],                   # optional
  "loan":          { … }                    # required
}
```

**Required scope:** `loans:write`.

**Idempotency-Key header:** required. See [Idempotency](/guides/idempotency.md).

**Import order:** the platform inserts `organisations` first (upserting on `(sourceSystem, externalId)`), then `collaterals`, then the `loan`. If validation fails midway, nothing is committed.

The full payload schema (with every field on every entity) is at [`HandoverPayload` in the API reference](/reference/openapi.md). What follows is the minimal shape.

### Minimal handover payload

```json
{
  "source": "my-integration",
  "organisations": [
    {
      "externalId": "borrower-001",
      "organisationName": "Test Borrower Holdings Pty Ltd",
      "structure": "PrivateCompany",
      "domicile": "au",
      "roles": ["borrower"],
      "industry": "PropertyDevelopment",
      "abn": "12345678901"
    }
  ],
  "collaterals": [
    {
      "externalId": "collateral-001",
      "collateralType": "cre",
      "propertyAddress": "1 Sample St, Sydney NSW 2000",
      "propertyValue": 2500000
    }
  ],
  "loan": {
    "externalId": "loan-001",
    "borrowerExternalId": "borrower-001",
    "collateralExternalIds": ["collateral-001"],
    "capitalProgramName": "Sandbox CRE Program",
    "productName": "CRE Senior 5yr",
    "principalAmount": 1500000,
    "currency": "AUD",
    "loanTerm": 60,
    "loanRank": "senior",
    "loanExitStrategy": "refinance",
    "purposeOfLoan": "acquisition"
  }
}
```

Every entity carries an **`externalId`** — your stable identifier from the source system. The platform uses `(yourApp, externalId)` as an upsert key, so re-sending the same payload with the same externalIds updates rather than duplicates. Combined with the required `Idempotency-Key` header, retries are safe.

### Response

Successful creation returns `201 Created` with an acknowledgement envelope:

```json
{
  "loan": { "id": "65f3...c4d5", "externalId": "loan-001" },
  "organisations": [ { "id": "65f3...c4f2", "externalId": "borrower-001" } ],
  "collaterals":   [ { "id": "65f3...c503", "externalId": "collateral-001" } ]
}
```

Follow up with `GET /api/v1/loans/<id>` to see the full DTO.

### Errors

| Status | When                                                                                                                                                              |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Payload validation failed — read the `message` for the specific field                                                                                             |
| `409`  | Conflict — either an idempotency-key mismatch (same key, different body) or a business-rule conflict (e.g. product bounds violated). The `message` disambiguates. |
| `500`  | Server-side ingest failure — the platform logs the specific cause; retry with a **new** idempotency key                                                           |

Retrying a `500` with the *same* idempotency key returns the same 500 (that's the point of idempotency). To actually re-attempt the write, use a fresh key. See [Idempotency](/guides/idempotency.md).

## Update

Updates via `PUT /api/v1/loans/{id}` are **not exposed in the public API today**. Most fields on a loan are servicing state — they change in response to platform events, not integrator writes. If you need a field to change:

* **Financial state** (principal, arrears, current LVR): produced by servicing events; not directly settable.
* **Origination state** (status transitions, credit decisions): happens inside the platform's origination flow; use the portal.
* **Free-form data** (comments, tags): use the platform's UI or the internal admin API — not part of the public developer surface yet.

If your integration needs a specific field to become writable, tell us.

## Delete

Not exposed. Loans are audited; deletion in the loan-management sense means `status = written_off` or `complete` — both of which are driven by platform events, not API writes.

## What to do next

* Wire up [**webhooks**](/webhooks/subscribe.md) for `loan.created`, `loan.updated`, `loan.status_changed`, and `loan.assessment_completed` so you're notified of changes rather than polling.
* Attach borrowers first: [**Organisations**](/resources/organisations.md) and [**Individuals**](/resources/individuals.md) cover the counterparty side.
* Full field list per response: [**API reference**](/reference/openapi.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developers.gallantreecapital.com/resources/loans.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
