> ## Documentation Index
> Fetch the complete documentation index at: https://docs.scrip.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Lots & Expiration

> Track individual credits with expiration and vesting dates

A lot is an individual credit recorded as its own entry in the ledger, with its own balance, expiration date, and vesting period. Instead of maintaining a single running total, `LOT`-mode assets track every credit separately.

This is useful when points need to expire after a fixed window (e.g., 12 months from issuance), when rewards should vest before becoming spendable (e.g., a referral bonus that unlocks after 30 days), when auth and settlement events need to match by `reference_id` (the [auth/settlement pattern](/guides/balance-operations#auth--settlement-pattern)), or when reporting needs to preserve the issuer of redeemed value.

Assets configured with `SIMPLE` mode do not create lots. They track a single balance per bucket. See [SIMPLE vs LOT Mode](#simple-vs-lot-mode) for a comparison.

## How Lots Work

When a `LOT`-mode asset is credited, a new lot is created:

```json theme={null}
{
  "id": "lot-uuid",
  "amount": "100",
  "remaining": "100",
  "status": "AVAILABLE",
  "created_at": "2025-01-15T10:00:00Z",
  "expires_at": "2026-01-15T10:00:00Z",
  "matures_at": null
}
```

When funds are debited, the oldest eligible lot is spent down before the next one is touched. Each lot tracks its remaining balance independently.

## Lot Lifecycle

A lot moves through a series of statuses from creation to consumption or expiration.

```
DEFERRED
    │ matures_at passes
    ▼
AVAILABLE ──┬── debit ──────────────> CONSUMED
            ├── hold ───────────────> HELD
            └── expires_at passes ──> EXPIRED

HELD ───────┬── release ────────────> AVAILABLE
            └── debit ──────────────> CONSUMED
```

| Status      | Spendable | Description                                                                                            |
| ----------- | --------- | ------------------------------------------------------------------------------------------------------ |
| `DEFERRED`  | No        | Lot has a future `matures_at`. Automatically transitions to `AVAILABLE` when the maturity date passes. |
| `AVAILABLE` | Yes       | Lot is mature and available for spending                                                               |
| `HELD`      | No        | Lot is reserved (e.g., pending settlement)                                                             |
| `CONSUMED`  | No        | Lot fully spent via debits                                                                             |
| `EXPIRED`   | No        | `expires_at` has passed, forfeited to breakage                                                         |

## Expiration

Set `expires_at` on a `CREDIT` action to give lots a deadline. The value can be a relative duration or a fixed timestamp. To see when outstanding value will expire across a program or asset, for breakage estimation or forecasting, use the [expiration schedule report](/api-reference/reporting/get-expiration-schedule).

A relative duration starts from the time each lot is created:

```json theme={null}
{"type": "CREDIT", "asset_id": "...", "amount": "100", "expires_at": "365d"}
```

A fixed timestamp sets the same deadline for all lots created by that rule:

```json theme={null}
{"type": "CREDIT", "asset_id": "...", "amount": "100", "expires_at": "2025-12-31T23:59:59Z"}
```

Expired lots are automatically excluded from debit operations.

Durations accept `h`, `d` (24 hours), and `w` (168 hours) units, plus the standard Go units down to seconds and below (`m`, `s`, `ms`). Units can be combined (`"1w2d12h"`) and fractional values are allowed. Months and years are not accepted; use days instead (`"365d"`).

<Warning>
  `m` means minutes, not months. `"3m"` parses as 3 minutes. For three months, write `"90d"`.
</Warning>

| Duration | Value    |
| -------- | -------- |
| 24 hours | `"24h"`  |
| 7 days   | `"7d"`   |
| 30 days  | `"30d"`  |
| 90 days  | `"90d"`  |
| 1 year   | `"365d"` |

## Vesting

Set `matures_at` on a `CREDIT` action to create a vesting period. Lots credited with a future `matures_at` land in `DEFERRED` status and automatically transition to `AVAILABLE` when the date passes. Deferred lots are excluded from debit operations.

```json theme={null}
{"type": "CREDIT", "asset_id": "...", "amount": "100", "matures_at": "30d"}
```

Both fields can be combined. This lot vests after 7 days and expires after 90:

```json theme={null}
{"type": "CREDIT", "asset_id": "...", "amount": "100", "matures_at": "7d", "expires_at": "90d"}
```

`DEFERRED` cannot be targeted as a bucket for writes. It is read-only and available as a filter in query endpoints (e.g., `status=DEFERRED` on the lots list, or `bucket=DEFERRED` on journal entries).

## Oldest-First Spending

When debiting a `LOT`-mode asset, Scrip spends the oldest eligible lots first:

```
Available lots (sorted by created_at):
  Lot A: 50 remaining (created Jan 1)    <- spent first
  Lot B: 100 remaining (created Feb 1)
  Lot C: 75 remaining (created Mar 1)

Debit 120:
  Lot A: 50 -> 0 (spent)
  Lot B: 100 -> 30 (partial)
  Lot C: 75 (untouched)
```

Only lots that are mature and not expired are eligible for consumption.

## Viewing Lots

Inspect a participant's lots for a specific asset:

```bash theme={null}
GET /v1/participants/{id}/balances/lots?asset_id=asset-uuid
```

| Filter           | Description                                                                   |
| ---------------- | ----------------------------------------------------------------------------- |
| `status`         | Filter by lot status (`DEFERRED`, `AVAILABLE`, `HELD`, `CONSUMED`, `EXPIRED`) |
| `reference_id`   | Filter by correlation ID (e.g., find lots belonging to a specific hold)       |
| `expires_before` | Lots expiring before a date                                                   |
| `expires_after`  | Lots expiring after a date                                                    |

## Lot-Aware Operations

Hold, release, and forfeit operations on `LOT`-mode assets are lot-aware:

| Action    | Behavior                                                                |
| --------- | ----------------------------------------------------------------------- |
| `HOLD`    | Moves lots from `AVAILABLE` to `HELD`, preserving metadata and lot UUID |
| `RELEASE` | Moves lots from `HELD` back to `AVAILABLE`, preserving lot UUID         |
| `FORFEIT` | Moves lots to `SYSTEM_BREAKAGE`                                         |

Lot bucket transitions (`AVAILABLE` ↔ `HELD`) update the lot row in place, preserving the lot UUID across holds, releases, and settles. Partial transitions split the remainder into a new lot.

These operations return a `lots_processed` array showing which lots were affected and by how much:

```json theme={null}
{
  "lots_processed": [
    {"lot_id": "lot-uuid-1", "amount": "50"},
    {"lot_id": "lot-uuid-2", "amount": "70"}
  ]
}
```

## SIMPLE vs LOT Mode

The asset's `inventory_mode` determines whether credits are tracked individually or as a running total.

| Feature                  | `SIMPLE`                             | `LOT`                                                                                                  |
| ------------------------ | ------------------------------------ | ------------------------------------------------------------------------------------------------------ |
| Balance tracking         | Single balance per bucket            | Individual lot balances                                                                                |
| Expiration               | Not supported                        | Per-lot expiration                                                                                     |
| Vesting                  | Not supported                        | Per-lot maturity dates                                                                                 |
| Spending order           | N/A                                  | Oldest eligible lots first                                                                             |
| Auth/settlement matching | Amount-based holds and releases only | [`reference_id` matching and auto-reconciliation](/guides/balance-operations#auth--settlement-pattern) |
| Issuer attribution       | Not supported                        | Preserved issuer lineage for redemption attribution                                                    |
| Per-credit provenance    | Not tracked                          | Tracked on each lot                                                                                    |

Use `SIMPLE` for balances with no per-credit lifecycle or provenance needs. Use `LOT` when expiration, vesting, auth/settlement matching, oldest-first spending, issuer attribution, or partner settlement matters.

<Note>
  `expires_at` and `matures_at` fields on `CREDIT` actions are silently ignored for `SIMPLE`-mode assets. No error is returned.
</Note>
