# Register and Track Loan Portfolios

Feature availability
The Loans API is only available to merchants with the **Managed Collections** feature enabled on their account. If you receive a `403 Forbidden` response, contact your Belvo account manager to have Managed Collections enabled.

Belvo's Loans API lets you register loan portfolio snapshots for direct debit customers. Each snapshot is both a record of your loan's current state **and** a collection instruction — as soon as you POST a snapshot, Belvo automatically attempts a direct debit collection for that customer on the same day (or the next business day), according to your Managed Collections arrangement.

The general flow is:

1. **POST a loan snapshot** — provide the current state of the loan, including the `defaultDate` (the date the loan became overdue).
2. **Belvo collects immediately** — no further action required from you. Belvo triggers the direct debit on the same day as your request (or the next business day).
3. **Receive a webhook** — Belvo notifies you of the collection outcome.
4. **POST a new snapshot** — each time you want Belvo to attempt another collection, send a new snapshot.


```mermaid
sequenceDiagram
    participant App as Your Application
    participant Belvo as Belvo

    Note over App,Belvo: 1. Register a loan snapshot (first time for a customer)
    App->>Belvo: POST /loans
    Note over Belvo: Creates loan + first snapshot internally
    Belvo-->>App: 201 Created + snapshotId

    Note over App,Belvo: 2. Register a subsequent snapshot
    App->>Belvo: POST /loans
    Note over Belvo: Adds snapshot to existing loan
    Belvo-->>App: 201 Created + snapshotId

    Note over App,Belvo: 3. Belvo auto-collects (same day or next business day)
    Note over Belvo: Belvo triggers direct debit collection
    Belvo-->>App: WEBHOOK collection outcome

    Note over App,Belvo: 4. Review loan history
    App->>Belvo: GET /loans?merchant_customer_id=CUST001234
    Belvo-->>App: 200 OK + list of snapshots
```

## Prerequisites

Before you begin, make sure you:

1. Generate your API keys.
2. Set up a webhook to listen to events.
3. Have an existing customer and payment method created. See Direct Debit Payments via API for setup instructions.


## How loan snapshots work

Each time you make a `POST /loans` request, Belvo creates a **loan snapshot** — a point-in-time record of the loan's current state. Belvo automatically sets the collection date (`targetCollectionDate`) to the current day or the next business day — you cannot specify it in the request.

- **First POST** for a `merchantCustomerId` + `paymentMethodId` pair: Belvo creates a parent loan record in the background and attaches the snapshot to it. You won't interact with the parent loan directly.
- **Subsequent POSTs** for the same pair: Belvo attaches the new snapshot to the existing loan. This gives you an audit trail of every collection request you've made.


One snapshot per customer per day
Each snapshot is uniquely identified by `merchantCustomerId` + the auto-assigned `targetCollectionDate`. Since `targetCollectionDate` is always set to the current day, you can only POST one snapshot per customer per day. A duplicate request on the same day returns a `409 Conflict`.

## Create a loan snapshot

To schedule a collection, make a POST Create a loan snapshot request:

```json Create a Loan Snapshot Request Body
{
  "paymentMethodId": "43e5a5b1-b2c6-4f45-8c1f-f28fec707a4b",
  "merchantCustomerId": "CUST001234",
  "daysOverdue": 45,
  "totalBalance": 15000.50,
  "principalAmount": 12000.00,
  "defaultDate": "2026-03-01",
  "issueDate": "2026-01-15"
}
```

#### Required fields

| Parameter  | Type | Required | Description | Example |
|  --- | --- | --- | --- | --- |
| `paymentMethodId` | string (uuid) | Yes | The ID of the payment method to debit. Must exist and belong to your merchant account. | `43e5a5b1-b2c6-4f45-8c1f-f28fec707a4b` |
| `merchantCustomerId` | string | Yes | Your unique alphanumeric identifier for the customer. Maximum 50 characters. | `CUST001234` |
| `daysOverdue` | integer | Yes | The number of days the loan is overdue at the time of this snapshot. Must be 0 or greater. | `45` |
| `totalBalance` | number | Yes | The total outstanding balance at the time of this snapshot. Must be positive and not exceed 9999999999.99. | `15000.50` |
| `principalAmount` | number | Yes | The principal amount of the loan. Must be positive and not exceed 9999999999.99. | `12000.00` |
| `defaultDate` | string (date) | Yes | The date the loan became overdue, in `YYYY-MM-DD` format. | `2026-03-01` |
| `issueDate` | string (date) | Yes | The date the loan was originally issued, in `YYYY-MM-DD` format. | `2026-01-15` |


For all optional parameters, see the POST Create a loan snapshot API reference.

#### Response

A successful request returns a `201 Created` response with the ID of the newly created snapshot:

```json Create a Loan Snapshot Response (201 Created)
{
  "id": "7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d"
}
```

Save this `id` — you can use it to retrieve the snapshot details later.

defaultDate is not returned in GET responses
`defaultDate` is accepted in the POST request body but is not included in the response from the List or Get endpoints. This is a known API limitation.

## List loan snapshots

To retrieve all snapshots, make a List all loan snapshots request.

We recommend filtering by `merchant_customer_id` to scope results to a single customer:

```
GET /loans?merchant_customer_id=CUST001234
```

This returns a flat list of every snapshot posted for that customer, across all their payment methods, in reverse chronological order.

## Get a loan snapshot

To retrieve a specific snapshot, make a Get a loan snapshot's details request using the snapshot `id` returned when you created it:

```
GET /loans/{id}
```

## Collection webhooks

When Belvo attempts the direct debit collection triggered by a loan snapshot, you receive a `payment_request_update` webhook with the outcome.

Successful collection
```json Webhook Payload — Successful Collection
{
  "eventType": "payment_request_update",
  "eventCode": "payment_request_successful",
  "datetime": "2026-06-10T12:34:56.789Z",
  "details": {
    "id": "3118128a-6792-4b06-bd61-4acf6f6ad6b5",
    "reference": "your_reference_here",
    "status": "successful",
    "amount": 15000.50,
    "failedReason": null,
    "failedMessage": null
  }
}
```

Failed collection
```json Webhook Payload — Failed Collection
{
  "eventType": "payment_request_update",
  "eventCode": "payment_request_failed",
  "datetime": "2026-06-10T12:34:56.789Z",
  "details": {
    "id": "3118128a-6792-4b06-bd61-4acf6f6ad6b5",
    "reference": "your_reference_here",
    "status": "failed",
    "amount": 15000.50,
    "failedReason": "01",
    "failedMessage": "Cuenta inexistente"
  }
}
```

The `details.id` in the webhook payload is the payment request ID, not the loan snapshot ID. For the full list of `failedReason` codes, see Bank Error Codes.

For general information on webhook setup, retry policy, and payload fields, see Webhooks.