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), 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 for a comparison.
How Lots Work
When a LOT-mode asset is credited, a new lot is created:
{
"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.
A relative duration starts from the time each lot is created:
{"type": "CREDIT", "asset_id": "...", "amount": "100", "expires_at": "365d"}
A fixed timestamp sets the same deadline for all lots created by that rule:
{"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").
m means minutes, not months. "3m" parses as 3 minutes. For three months, write "90d".
| 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.
{"type": "CREDIT", "asset_id": "...", "amount": "100", "matures_at": "30d"}
Both fields can be combined. This lot vests after 7 days and expires after 90:
{"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:
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:
{
"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 |
| 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.
expires_at and matures_at fields on CREDIT actions are silently ignored for SIMPLE-mode assets. No error is returned.