Skip to main content
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), or when you need to spend oldest points first for regulatory or accounting reasons. 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, lots are consumed in FIFO (first in, first out) order: the oldest 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 > now)
    ↓ matures_at passes
AVAILABLE (spendable)
    ↓ hold
HELD (reserved)
    ↓ debit
CONSUMED (fully spent)
    ↓ or expires_at < now
EXPIRED (forfeited to breakage)
StatusSpendableDescription
DEFERREDNoLot has a future matures_at. Automatically transitions to AVAILABLE when the maturity date passes.
AVAILABLEYesLot is mature and available for spending
HELDNoLot is reserved (e.g., pending settlement)
CONSUMEDNoLot fully spent via debits
EXPIREDNoexpires_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. A relative duration starts from the time each lot is created:
{"type": "CREDIT", "asset_id": "...", "amount": "100", "expires_at": "8760h"}
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 are specified in hours.
DurationValue
24 hours"24h"
7 days"168h"
30 days"720h"
90 days"2160h"
1 year"8760h"

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": "720h"}
Both fields can be combined. This lot vests after 7 days and expires after 90:
{"type": "CREDIT", "asset_id": "...", "amount": "100", "matures_at": "168h", "expires_at": "2160h"}
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).

FIFO Consumption

When debiting a LOT-mode asset, lots are consumed oldest-first:
Available lots (sorted by created_at):
  Lot A: 50 remaining (created Jan 1)    ← consumed first
  Lot B: 100 remaining (created Feb 1)
  Lot C: 75 remaining (created Mar 1)

Debit 120:
  Lot A: 50 → 0 (consumed)
  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
FilterDescription
statusFilter by lot status (DEFERRED, AVAILABLE, HELD, CONSUMED, EXPIRED)
reference_idFilter by correlation ID (e.g., find lots belonging to a specific hold)
expires_beforeLots expiring before a date
expires_afterLots expiring after a date

Lot-Aware Operations

Hold, release, and forfeit operations on LOT-mode assets are lot-aware:
ActionBehavior
HOLDMoves lots from AVAILABLE to HELD, preserving metadata and lot UUID
RELEASEMoves lots from HELD back to AVAILABLE, preserving lot UUID
FORFEITMoves lots to SYSTEM_BREAKAGE
Lot bucket transitions (AVAILABLEHELD) 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.
FeatureSIMPLELOT
Balance trackingSingle balance per bucketIndividual lot balances
ExpirationNot supportedPer-lot expiration
VestingNot supportedPer-lot maturity dates
Consumption orderN/AFIFO (oldest first)
Use SIMPLE for points with no lifecycle. Use LOT when you need expiration, vesting, or vintage tracking.
expires_at and matures_at fields on CREDIT actions are silently ignored for SIMPLE-mode assets. No error is returned.