Core concepts
Wallets & Points
Every customer has a wallet that tracks their loyalty point balance. The wallet is backed by an append-only ledger that records every point movement with a full audit trail.
Wallet overview
| Field | Description |
|---|---|
id | Unique wallet ID |
customer_id | The customer this wallet belongs to |
balance | Current point balance (integer) |
expiring_soon | Points expiring in the near future |
A wallet is automatically created when a customer is registered. There is exactly one wallet per customer.
Ledger entries
The ledger is an immutable, append-only record of every point movement. Each entry contains:
| Field | Description |
|---|---|
type | The type of movement: earn, adjust, redeem, or expire |
amount | Signed integer: positive for credits, negative for debits |
running_balance | The wallet balance after this entry |
reason | Human-readable explanation |
causer_type | What triggered this entry (e.g., transaction, admin user) |
causer_id | ID of the triggering entity |
transaction_id | Associated transaction (for earn entries) |
created_at | Timestamp of the entry |
Entry types
| Type | Direction | Triggered by |
|---|---|---|
| earn | Positive | Transaction submitted. Points calculated from earn rules |
| adjust | Positive or negative | Manual balance adjustment by admin or API |
| redeem | Negative | Customer redeems a reward |
| expire | Negative | Points expired under the vendor's expiration policy |
Immutable ledger
Ledger entries are never updated or deleted. The running_balance field on each entry provides a verifiable audit trail. To correct a mistake, create a new adjustment entry rather than modifying existing entries.
Point batches and expiration
When a vendor has point expiration enabled, earned points are tracked in batches. Each batch records:
| Field | Description |
|---|---|
amount | Points in this batch |
consumed | Points already used from this batch |
expires_at | When the remaining points in this batch expire |
Points are consumed from the oldest batches first (FIFO). When a batch expires, any remaining points are deducted from the wallet and recorded as an expire ledger entry.
How expiration works
- Vendor enables expiration and sets
expiration_months(e.g., 12 months) - Each time points are earned, a batch is created with
expires_atset to the earn date + expiration months - When points are redeemed or adjusted, the oldest batches are consumed first
- A background job periodically checks for expired batches and creates
expireentries
Manual adjustments
Administrators can manually adjust a wallet balance through the admin portal or the API. Adjustments create a new ledger entry with type adjust:
POST /api/v1/customers/{customer}/wallet/adjust
{
"amount": 500,
"reason": "Compensation for service issue",
"idempotency_key": "goodwill-2024-0042"
}
Positive amounts add points; negative amounts deduct points. The
idempotency_key makes the call safe to retry: re-sending the same key for the
same customer returns the original ledger entry instead of moving the balance
twice.
See Wallet endpoints for the complete API reference.