# Settle a lease

> The leases.settle operation: POST /api/v1/leases/{id}/settle in the REST API and the leases_settle tool in the MCP server.

Charges what your service consumed from a lease since the previous settle (`consumed`), with a
`usageSummary`: one line per meter (and attribute set) with the `quantity` used and its `amount`; the amounts
add up to `consumed`. Each line is stored as a usage event and counted in the daily usage aggregates. The
settle is charged at once (the lease's `captured` grows); the ledger books what a lease's settles charged as
usage revenue per meter in one posting, when the lease closes, is renewed or expires. Wallet usage can't use a
meter that a metered price bills in arrears (prepaid and arrears usage coexist on different meters).

Then the lease:

- stays open (neither `final` nor `renew`): keep consuming its `remaining`;
- closes (`final: true`): what it still holds goes back to the wallet;
- is renewed (`renew`): it closes, and a new lease with a new ID, a fresh TTL and the same expiry terms is
  reserved, for `renew.amount` (default: the same amount) or as much of it as the wallet has.
  `renewedLease` is null when the wallet can't fund it.

A settle after `expiresAt` gets `lease_expired`, except within the late-settle window of a
`hold_for_late_settle` lease: that settle charges `consumed` and closes the lease (`late: true`).

Ordering and retries: a resent settle (same `Idempotency-Key` and content) is answered with `duplicate: true`
and changes nothing. Optionally number your settles with `settleSeq` (the lease's `settleSeq` plus one): a
numbered settle is applied only after the one before it (`lease_settle_out_of_order`, retryable, otherwise),
so a `final` settle can't overtake earlier usage, and a settle numbered at or below the lease's `settleSeq`
is a duplicate. Every response has the lease's `suggestedNextAmount`.

## At a glance

- **REST:** `POST /api/v1/leases/{id}/settle`
- **MCP tool:** `leases_settle`
- **Operation ID:** `leases.settle`
- **Scope:** Brand
- **Permission:** `wallets:write`
- **Risk tier:** `write`
- **Dry run:** Supported (send `Ownrecord-Dry-Run: true`)
- **Idempotency:** `Idempotency-Key` required
- **Modes:** Test and live
- **Authentication:** API key with the `wallets:write` scope
- **Since:** M6

## Request

### Path parameters

| Field | Type         | Description            |
| ----- | ------------ | ---------------------- |
| `id`  | `lease_…` ID | The lease (`lease_…`). |

### Body

A JSON object (`Content-Type: application/json`):

| Field                            | Type                           | Required | Description                                                                                                                                           |
| -------------------------------- | ------------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `consumed`                       | object                         | Yes      | What was used since the previous settle of this lease, in its currency; at most what it still holds.                                                  |
| `consumed.amount`                | string                         | Yes      | Integer micros as a string (5000000 = 5.00); finer than a cent is fine.                                                                               |
| `consumed.currency`              | string                         | Yes      | ISO 4217 currency code: the wallet's currency.                                                                                                        |
| `usageSummary`                   | array of object                | No       | Up to 100 lines, one per meter (and attribute set), whose amounts add up to `consumed`. Each is stored as a usage event.                              |
| `usageSummary[].meter`           | string                         | Yes      | The meter this usage counts toward, e.g. `api.calls`. Not a meter a metered price bills in arrears.                                                   |
| `usageSummary[].quantity`        | string or integer (at least 0) | Yes      | How much of the meter was used, in its unit, as an integer string (e.g. `"1200"` calls).                                                              |
| `usageSummary[].amount`          | object                         | Yes      | What this usage cost, in the wallet's currency.                                                                                                       |
| `usageSummary[].amount.amount`   | string                         | Yes      | Integer micros as a string (5000000 = 5.00); finer than a cent is fine.                                                                               |
| `usageSummary[].amount.currency` | string                         | Yes      | ISO 4217 currency code: the wallet's currency.                                                                                                        |
| `usageSummary[].attributes`      | object                         | No       | Optional dimensions of the event, e.g. `{ "model": "large" }`; usage is aggregated per day and attribute set.                                         |
| `final`                          | boolean                        | No       | true: close the lease; what it still holds goes back to the wallet.                                                                                   |
| `renew`                          | object                         | No       | Close this lease and continue in a new one (new ID, fresh TTL, same expiry terms). Send `{}` to keep the amount and TTL.                              |
| `renew.amount`                   | object                         | No       | The new lease's amount (default: this lease's amount).                                                                                                |
| `renew.amount.amount`            | string                         | Yes      | Integer micros as a string (5000000 = 5.00); finer than a cent is fine.                                                                               |
| `renew.amount.currency`          | string                         | Yes      | ISO 4217 currency code: the wallet's currency.                                                                                                        |
| `renew.ttlSeconds`               | integer (1 to 900)             | No       | The new lease's TTL (default: this lease's TTL).                                                                                                      |
| `settleSeq`                      | integer (at least 1)           | No       | Optional: this settle's number, the lease's `settleSeq` plus one. Numbered settles are applied in order, and a resent one is answered as a duplicate. |

## Response

`200 OK` with a JSON object:

| Field                           | Type                                                                  | Description                                                                                                                                                                                                  |
| ------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `object`                        | `"lease_settlement"`                                                  |                                                                                                                                                                                                              |
| `settleSeq`                     | integer (at least 1)                                                  | This settle's number on the lease.                                                                                                                                                                           |
| `duplicate`                     | boolean                                                               | true: this settle had been applied before (resent, or numbered at or below the lease's `settleSeq`); nothing changed now, and `lease` is the lease as it is now.                                             |
| `late`                          | boolean                                                               | Settled after `expiresAt`, inside the late-settle window; it closed the lease.                                                                                                                               |
| `consumed`                      | money                                                                 | What this settle captured.                                                                                                                                                                                   |
| `lease`                         | object                                                                | The settled lease.                                                                                                                                                                                           |
| `lease.id`                      | `lease_…` ID                                                          |                                                                                                                                                                                                              |
| `lease.object`                  | `"lease"`                                                             |                                                                                                                                                                                                              |
| `lease.brandId`                 | `brd_…` ID                                                            |                                                                                                                                                                                                              |
| `lease.livemode`                | boolean                                                               |                                                                                                                                                                                                              |
| `lease.walletId`                | `wal_…` ID                                                            |                                                                                                                                                                                                              |
| `lease.status`                  | one of `active`, `expired_pending`, `captured`, `released`, `expired` | `active`: settle against it. `expired_pending`: past its TTL, held for a late settle until `lateSettleUntil`. `captured`: closed by a settle (`final` or a renewal). `expired`: closed by its expiry policy. |
| `lease.amount`                  | money                                                                 | How much was leased.                                                                                                                                                                                         |
| `lease.captured`                | money                                                                 | How much the settles (and an expiry capture) have charged so far. The ledger books it as usage revenue when the lease closes, is renewed or expires.                                                         |
| `lease.remaining`               | money                                                                 | What can still be consumed: `amount` − `captured` while the lease is open, 0 once it is closed.                                                                                                              |
| `lease.ttlSeconds`              | integer (1 to 900)                                                    |                                                                                                                                                                                                              |
| `lease.expiresAt`               | date-time                                                             | Settle before this instant; then the expiry policy applies.                                                                                                                                                  |
| `lease.expiryPolicy`            | one of `release`, `capture`, `hold_for_late_settle`                   |                                                                                                                                                                                                              |
| `lease.lateSettleWindowSeconds` | integer or null                                                       | `hold_for_late_settle` only.                                                                                                                                                                                 |
| `lease.lateSettleFallback`      | one of `release`, `capture` or null                                   | `hold_for_late_settle` only.                                                                                                                                                                                 |
| `lease.lateSettleUntil`         | date-time or null                                                     | `hold_for_late_settle` only: a settle is accepted until this instant (`expiresAt` + window).                                                                                                                 |
| `lease.settleSeq`               | integer (at least 0)                                                  | How many settles the lease has had.                                                                                                                                                                          |
| `lease.renewedFrom`             | `lease_…` ID or null                                                  | The lease this one renewed.                                                                                                                                                                                  |
| `lease.renewedTo`               | `lease_…` ID or null                                                  | The lease a renewing settle replaced this one with.                                                                                                                                                          |
| `lease.suggestedNextAmount`     | money                                                                 | Sizing guidance for the next lease (or renewal) of this TTL, from the recent consumption rate with 25% headroom.                                                                                             |
| `lease.metadata`                | object                                                                |                                                                                                                                                                                                              |
| `lease.createdAt`               | date-time                                                             |                                                                                                                                                                                                              |
| `lease.closedAt`                | date-time or null                                                     |                                                                                                                                                                                                              |
| `renewedLease`                  | object or null                                                        | The new lease of a renewal. null without `renew`, after a late settle, or when the wallet couldn't fund it (frozen, or no spendable credits); its amount may be less than asked when the wallet had less.    |
| `usageEventIds`                 | array of `uev_…` ID                                                   | The usage events, one per usage summary line.                                                                                                                                                                |

## Examples

### Settle usage of two meters and keep the lease

```http
POST /api/v1/leases/lease_01m4v0d9k2j7r5t3w8x6y4z2b0/settle
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "consumed": {
    "amount": "1250000",
    "currency": "EUR"
  },
  "usageSummary": [
    {
      "meter": "api.calls",
      "quantity": "1000",
      "amount": {
        "amount": "1000000",
        "currency": "EUR"
      },
      "attributes": {
        "model": "large"
      }
    },
    {
      "meter": "tokens.out",
      "quantity": "25000",
      "amount": {
        "amount": "250000",
        "currency": "EUR"
      }
    }
  ],
  "settleSeq": 1
}
```

Response (`200`):

```json
{
  "object": "lease_settlement",
  "settleSeq": 1,
  "duplicate": false,
  "late": false,
  "consumed": {
    "amount": "1250000",
    "currency": "EUR"
  },
  "lease": {
    "id": "lease_01m4v0d9k2j7r5t3w8x6y4z2b0",
    "object": "lease",
    "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
    "livemode": false,
    "walletId": "wal_01m4ty3c2b5qx7mhd9k8w6s0b1",
    "status": "active",
    "amount": {
      "amount": "5000000",
      "currency": "EUR"
    },
    "captured": {
      "amount": "1250000",
      "currency": "EUR"
    },
    "remaining": {
      "amount": "3750000",
      "currency": "EUR"
    },
    "ttlSeconds": 300,
    "expiresAt": "2026-10-13T12:05:00.000Z",
    "expiryPolicy": "release",
    "lateSettleWindowSeconds": null,
    "lateSettleFallback": null,
    "lateSettleUntil": null,
    "settleSeq": 1,
    "renewedFrom": null,
    "renewedTo": null,
    "suggestedNextAmount": {
      "amount": "7812500",
      "currency": "EUR"
    },
    "metadata": {
      "worker": "api-eu-1"
    },
    "createdAt": "2026-10-13T12:00:00.000Z",
    "closedAt": null
  },
  "renewedLease": null,
  "usageEventIds": ["uev_01m4v0d9k2j7r5t3w8x6y4z2b2", "uev_01m4v0d9k2j7r5t3w8x6y4z2b3"]
}
```

### Settle and renew before the TTL

The lease closes and a new 5.00 lease continues with a fresh TTL.

```http
POST /api/v1/leases/lease_01m4v0d9k2j7r5t3w8x6y4z2b0/settle
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "consumed": {
    "amount": "3500000",
    "currency": "EUR"
  },
  "usageSummary": [
    {
      "meter": "api.calls",
      "quantity": "3500",
      "amount": {
        "amount": "3500000",
        "currency": "EUR"
      }
    }
  ],
  "renew": {},
  "settleSeq": 2
}
```

Response (`200`):

```json
{
  "object": "lease_settlement",
  "settleSeq": 2,
  "duplicate": false,
  "late": false,
  "consumed": {
    "amount": "3500000",
    "currency": "EUR"
  },
  "lease": {
    "id": "lease_01m4v0d9k2j7r5t3w8x6y4z2b0",
    "object": "lease",
    "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
    "livemode": false,
    "walletId": "wal_01m4ty3c2b5qx7mhd9k8w6s0b1",
    "status": "captured",
    "amount": {
      "amount": "5000000",
      "currency": "EUR"
    },
    "captured": {
      "amount": "4750000",
      "currency": "EUR"
    },
    "remaining": {
      "amount": "0",
      "currency": "EUR"
    },
    "ttlSeconds": 300,
    "expiresAt": "2026-10-13T12:05:00.000Z",
    "expiryPolicy": "release",
    "lateSettleWindowSeconds": null,
    "lateSettleFallback": null,
    "lateSettleUntil": null,
    "settleSeq": 2,
    "renewedFrom": null,
    "renewedTo": "lease_01m4v0d9k2j7r5t3w8x6y4z2b1",
    "suggestedNextAmount": {
      "amount": "7421875",
      "currency": "EUR"
    },
    "metadata": {
      "worker": "api-eu-1"
    },
    "createdAt": "2026-10-13T12:00:00.000Z",
    "closedAt": "2026-10-13T12:04:00.000Z"
  },
  "renewedLease": {
    "id": "lease_01m4v0d9k2j7r5t3w8x6y4z2b1",
    "object": "lease",
    "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
    "livemode": false,
    "walletId": "wal_01m4ty3c2b5qx7mhd9k8w6s0b1",
    "status": "active",
    "amount": {
      "amount": "5000000",
      "currency": "EUR"
    },
    "captured": {
      "amount": "0",
      "currency": "EUR"
    },
    "remaining": {
      "amount": "5000000",
      "currency": "EUR"
    },
    "ttlSeconds": 300,
    "expiresAt": "2026-10-13T12:09:00.000Z",
    "expiryPolicy": "release",
    "lateSettleWindowSeconds": null,
    "lateSettleFallback": null,
    "lateSettleUntil": null,
    "settleSeq": 0,
    "renewedFrom": "lease_01m4v0d9k2j7r5t3w8x6y4z2b0",
    "renewedTo": null,
    "suggestedNextAmount": {
      "amount": "7421875",
      "currency": "EUR"
    },
    "metadata": {
      "worker": "api-eu-1"
    },
    "createdAt": "2026-10-13T12:04:00.000Z",
    "closedAt": null
  },
  "usageEventIds": ["uev_01m4v0d9k2j7r5t3w8x6y4z2b2"]
}
```

### Close the lease

```http
POST /api/v1/leases/lease_01m4v0d9k2j7r5t3w8x6y4z2b0/settle
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "consumed": {
    "amount": "0",
    "currency": "EUR"
  },
  "usageSummary": [],
  "final": true
}
```

Response (`200`):

```json
{
  "object": "lease_settlement",
  "settleSeq": 1,
  "duplicate": false,
  "late": false,
  "consumed": {
    "amount": "0",
    "currency": "EUR"
  },
  "lease": {
    "id": "lease_01m4v0d9k2j7r5t3w8x6y4z2b0",
    "object": "lease",
    "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
    "livemode": false,
    "walletId": "wal_01m4ty3c2b5qx7mhd9k8w6s0b1",
    "status": "captured",
    "amount": {
      "amount": "5000000",
      "currency": "EUR"
    },
    "captured": {
      "amount": "0",
      "currency": "EUR"
    },
    "remaining": {
      "amount": "0",
      "currency": "EUR"
    },
    "ttlSeconds": 300,
    "expiresAt": "2026-10-13T12:05:00.000Z",
    "expiryPolicy": "release",
    "lateSettleWindowSeconds": null,
    "lateSettleFallback": null,
    "lateSettleUntil": null,
    "settleSeq": 1,
    "renewedFrom": null,
    "renewedTo": null,
    "suggestedNextAmount": {
      "amount": "500000",
      "currency": "EUR"
    },
    "metadata": {
      "worker": "api-eu-1"
    },
    "createdAt": "2026-10-13T12:00:00.000Z",
    "closedAt": "2026-10-13T12:01:00.000Z"
  },
  "renewedLease": null,
  "usageEventIds": []
}
```

## Errors

| Code                                                                                                           | HTTP status | When                                                                                              | What to do                                                                                                                                                                      |
| -------------------------------------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed)                         | 400         | A field is malformed, or a settle asks for both `final` and `renew`.                              | Fix the fields listed in `details.issues` and send the request again.                                                                                                           |
| [`lease_usage_mismatch`](https://ownrecord.io/docs/reference/errors.md#lease_usage_mismatch)                   | 400         | The amounts of the `usageSummary` lines don't add up to `consumed`.                               | Make the amounts of the `usageSummary` lines add up to `consumed` exactly (`details.consumed`, `details.usageTotal`, integer micros), in the lease's currency.                  |
| [`capture_exceeds_reservation`](https://ownrecord.io/docs/reference/errors.md#capture_exceeds_reservation)     | 400         | `consumed` is more than the lease still holds (`remaining`).                                      | Capture at most the reserved amount that is still open (`details.outstanding`, integer micros), or reserve more first.                                                          |
| [`wallet_currency_mismatch`](https://ownrecord.io/docs/reference/errors.md#wallet_currency_mismatch)           | 400         | `consumed` isn't in the lease's currency.                                                         | Use the wallet for this currency. A customer has one wallet per currency (`details.expected`, `details.actual`).                                                                |
| [`usage_meter_billed_in_arrears`](https://ownrecord.io/docs/reference/errors.md#usage_meter_billed_in_arrears) | 400         | A line names a meter a metered price bills in arrears.                                            | Report prepaid (wallet) usage on a meter that no metered price names, e.g. `api.calls.prepaid` (`details.meter`).                                                               |
| [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found)                       | 404         | No lease with this ID in the brand and mode.                                                      | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.                                                                            |
| [`lease_expired`](https://ownrecord.io/docs/reference/errors.md#lease_expired)                                 | 409         | The lease is past `expiresAt` (and past `lateSettleUntil` for `hold_for_late_settle`).            | Create a new lease for further usage. To settle usage after a crash, make leases with the expiry policy `hold_for_late_settle`, which accepts a settle until `lateSettleUntil`. |
| [`lease_closed`](https://ownrecord.io/docs/reference/errors.md#lease_closed)                                   | 409         | A `final` settle or a renewal already closed the lease (`details.renewedTo` names its successor). | Settle the lease it was renewed into (`details.renewedTo`), or create a new lease. A lease closes with a `final` settle or a renewal.                                           |
| [`lease_settle_out_of_order`](https://ownrecord.io/docs/reference/errors.md#lease_settle_out_of_order)         | 409         | `settleSeq` skips a settle that hasn't arrived yet (retryable).                                   | Send the settles of a lease in order, one at a time: `settleSeq` must be the lease's `settleSeq` plus one (`details.expected`). Retry once the earlier settle has been applied. |
| [`lease_settle_seq_conflict`](https://ownrecord.io/docs/reference/errors.md#lease_settle_seq_conflict)         | 409         | The lease's last settle has this `settleSeq` but other content.                                   | Give each settle of a lease the next number (`details.settleSeq` is the lease's current one) and resend a settle only unchanged.                                                |
| [`idempotency_key_reused`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_reused)               | 409         | The `Idempotency-Key` was already used for a different settle of this lease.                      | Use a new Idempotency-Key for a different request, or resend the original request unchanged.                                                                                    |
| [`permission_denied`](https://ownrecord.io/docs/reference/errors.md#permission_denied)                         | 403         | The credentials can't use this brand's wallets (`wallets:write`).                                 | Ask an organization owner or admin for a role or scope that allows this action.                                                                                                 |

It can also return these [common errors](https://ownrecord.io/docs/reference.md#common-errors): [`api_version_unsupported`](https://ownrecord.io/docs/reference/errors.md#api_version_unsupported), [`invalid_json`](https://ownrecord.io/docs/reference/errors.md#invalid_json), [`unsupported_media_type`](https://ownrecord.io/docs/reference/errors.md#unsupported_media_type), [`authentication_required`](https://ownrecord.io/docs/reference/errors.md#authentication_required), [`api_key_invalid`](https://ownrecord.io/docs/reference/errors.md#api_key_invalid), [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required), [`idempotency_key_required`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_required), [`idempotency_in_progress`](https://ownrecord.io/docs/reference/errors.md#idempotency_in_progress), [`approval_required`](https://ownrecord.io/docs/reference/errors.md#approval_required), [`approval_rejected`](https://ownrecord.io/docs/reference/errors.md#approval_rejected), [`approval_expired`](https://ownrecord.io/docs/reference/errors.md#approval_expired), [`stale_approval`](https://ownrecord.io/docs/reference/errors.md#stale_approval), [`approval_policy_denied`](https://ownrecord.io/docs/reference/errors.md#approval_policy_denied), [`rate_limited`](https://ownrecord.io/docs/reference/errors.md#rate_limited), [`internal_error`](https://ownrecord.io/docs/reference/errors.md#internal_error).
