Skip to main content

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.

While rules handle most balance changes automatically, you can also modify balances directly via the API for customer service, corrections, and manual workflows.

Adjust

Credit or debit a participant’s balance:
POST /v1/participants/{id}/balances/adjust
{
  "program_id": "program-uuid",
  "asset_id": "asset-uuid",
  "type": "CREDIT",
  "amount": "500",
  "description": "Customer service goodwill credit"
}
FieldRequiredDescription
program_idYesProgram context
asset_idYesWhich asset to adjust
typeYesCREDIT or DEBIT
amountYesPositive amount
allow_negativeNoDEBIT only. When true, allows the debit to overdraw the balance below zero. Used for clawbacks. Defaults to false.
bucketNoAVAILABLE (default) or HELD
descriptionYesReason for the adjustment (1-500 characters)
Amounts must be positive. Use type to control direction. DEBIT fails if the available balance is insufficient, unless allow_negative is set to true. If amount has more decimal places than the asset’s scale, the request is rejected with a 400 (invalid_scale). Round in your client before calling adjust.

Negative Balances

Set allow_negative to true on a DEBIT to let the balance go below zero. This is useful when you need to recover value that a participant has already spent:
  • Refund clawbacks. A cardholder earned cashback on a purchase that was later refunded. The cashback has already been redeemed, so the available balance is zero. A negative-balance debit records the debt.
  • Chargeback recovery. A payment is disputed and reversed, but the associated reward points were already used. The debit brings the balance negative until the participant earns enough to offset it.
  • Corrections. An incorrect credit was issued and the participant has already spent part of it. A negative-balance debit corrects the ledger without waiting for funds to replenish.
POST /v1/participants/{id}/balances/adjust
{
  "program_id": "program-uuid",
  "asset_id": "asset-uuid",
  "type": "DEBIT",
  "amount": "25",
  "allow_negative": true,
  "description": "Clawback: refund on order #4821"
}
For LOT-mode assets, the system consumes whatever lots are available first, then posts the remaining amount as an overdraft. The balance goes negative by the uncovered portion. allow_negative also works in rule actions. This lets your rules engine handle clawbacks automatically, for example debiting cashback when a refund event arrives, even if the participant’s balance is zero.
allow_negative only applies to adjustments and rule-based debits. Transfers and redemptions always require sufficient funds.

Hold

Reserve funds by moving them from AVAILABLE to HELD:
POST /v1/participants/{id}/balances/hold
{
  "program_id": "program-uuid",
  "asset_id": "asset-uuid",
  "amount": "500",
  "reference_id": "auth_78945",
  "description": "Auth hold - txn 78945"
}
FieldRequiredDescription
program_idYesProgram context
asset_idYesWhich asset to hold
amountYesPositive amount to reserve
reference_idNoCorrelation ID for this hold (LOT mode only). Stamps held lots so a future release can target them. 1-255 characters, alphanumeric plus ._:@-.
descriptionYesReason for the hold (1-500 characters)
Held funds are not spendable. Use holds for:
  • Authorization holds (reserve rewards until the transaction settles)
  • Fraud review (freeze funds pending investigation)
  • Pending approvals (hold until manual review completes)

Release

Move held funds back to AVAILABLE:
POST /v1/participants/{id}/balances/release
{
  "program_id": "program-uuid",
  "asset_id": "asset-uuid",
  "reference_id": "auth_78945",
  "description": "Settlement confirmed"
}
FieldRequiredDescription
program_idYesProgram context
asset_idYesWhich asset to release
amountNoAmount to release. If omitted, all matching held lots are released.
reference_idNoRelease only lots stamped with this reference during a previous hold (LOT mode only)
descriptionYesReason for the release (1-500 characters)
When reference_id is provided, only lots stamped with that reference during a previous hold are targeted. You can also filter by lot age using earned_from and earned_to (RFC 3339 timestamps) for batch-releasing held balances. LOT mode only.

Forfeit

Remove funds permanently from a participant’s balance:
POST /v1/participants/{id}/balances/forfeit
{
  "program_id": "program-uuid",
  "asset_id": "asset-uuid",
  "amount": "100",
  "bucket": "AVAILABLE",
  "description": "Expired points"
}
FieldRequiredDescription
program_idYesProgram context
asset_idYesWhich asset to forfeit
amountYesPositive amount
bucketYesAVAILABLE or HELD. Specifies which balance bucket to forfeit from.
descriptionYesReason for the forfeit (1-500 characters)
Forfeited funds move to the SYSTEM_BREAKAGE account.

Void Hold

Cancel HELD lots that were credited directly into HELD (e.g., pending authorization rewards) by reference_id, returning value to the original source account:
POST /v1/participants/{id}/balances/void-hold
{
  "program_id": "program-uuid",
  "asset_id": "asset-uuid",
  "reference_id": "auth_12345",
  "description": "Auth reversal"
}
FieldRequiredDescription
program_idYesProgram context
asset_idYesWhich asset to void (LOT mode only)
reference_idYesCorrelation ID matching the original CREDIT to HELD
descriptionYesReason for the void (1-500 characters)
Void hold is the correct operation for auth reversals. It cancels lots that were credited directly into the HELD bucket and returns value to the source (program wallet for PREFUNDED assets, SYSTEM_ISSUANCE for UNLIMITED).
Void hold only processes lots that were created directly in HELD via CREDIT. Lots moved to HELD via a HOLD operation (participant funds) are excluded for safety — returning those to the source would effectively confiscate participant value. Use release to return participant-held funds to AVAILABLE.
amount, lot_ids, earned_from, and earned_to are not supported — the entire provisional accrual matching the reference_id is voided.

Auth / Settlement Pattern

Card transactions follow a two-step lifecycle: the issuer-processor authorizes the transaction first, and the merchant settles (captures) it later — sometimes hours or days after. The settlement amount can differ from the authorization due to tips, partial captures, or currency conversion. Most programs can skip authorizations and credit rewards on settlement only. This is the simplest approach, and the Stripe Issuing example uses it. But if your program needs to show pending rewards to cardholders in real time — before the transaction settles — you can use the auth/settle pattern to hold provisional rewards at authorization and reconcile them when the merchant captures. This is the dual-message pattern common in card processing: both the authorization and settlement events flow into Scrip, and the system handles the difference between them automatically.
This pattern requires a LOT-mode asset. reference_id stamps individual lots at authorization so they can be matched at settlement. For SIMPLE-mode assets, use amount-based holds and releases without reference_id.
1

Authorization: hold provisional rewards

When a cardholder taps their card, your issuer-processor fires an authorization webhook. Your backend forwards it as an event with event.type == "auth". A rule credits points into the HELD bucket, stamping the lots with the authorization ID:
{"type": "CREDIT", "asset_id": "...", "amount": "event.amount", "bucket": "HELD", "reference_id": "event.authorization_id"}
The cardholder’s balance now shows pending rewards in the held field. These are not spendable.Alternatively, if the participant already has an AVAILABLE balance and you want to reserve existing points rather than mint new ones:
{"type": "HOLD", "asset_id": "...", "amount": "event.amount", "reference_id": "event.authorization_id"}
2

Settlement: reconcile and finalize

When the merchant captures the transaction, your backend sends a second event with event.type == "settlement". Two approaches:Auto-reconcile with CREDIT. Credit to AVAILABLE with the same reference_id. The system finds the held lots, consumes them, and creates new available lots for the settlement amount. If the amounts differ, the delta is handled automatically — extra funds are sourced from the program wallet (or system issuance for unlimited assets), and any excess is returned.
{"type": "CREDIT", "asset_id": "...", "amount": "event.settlement_amount", "reference_id": "event.authorization_id"}
This is the recommended approach when the settlement amount may differ from the authorization. If no held lots exist for the reference_id (e.g., the auth event was missed or not sent), the system falls back to a standard credit.Simple release. If the settlement amount always matches the authorization, a release is sufficient. This moves the held lots back to AVAILABLE without reconciliation:
{"type": "RELEASE", "asset_id": "...", "reference_id": "event.authorization_id"}
When amount is omitted with a reference_id, all lots held under that reference are released.
3

Or: void the authorization

If the authorization is reversed before settling, use VOID_HOLD to cancel the provisional rewards. This returns value to the source account (program wallet or system issuance):
{"type": "VOID_HOLD", "asset_id": "...", "reference_id": "event.authorization_id"}
For authorizations that simply expire without settling, set expires_at on the original CREDIT (e.g., "720h"), which automatically forfeits uncaptured holds after the expiration window.See the Stripe Issuing example for details on handling uncaptured authorizations.

How auto-reconciliation works

When a CREDIT to AVAILABLE includes a reference_id that matches existing HELD lots, the system reconciles automatically:
Settlement vs. authWhat happens
EqualHeld lots consumed, same amount credited to AVAILABLE
Settlement > auth (over-capture, e.g., tip added)Extra amount sourced from program wallet or system issuance
Settlement < auth (under-capture, e.g., partial capture)Excess returned to program wallet or system issuance
No held lots found for the reference_idStandard credit applied with reference_id stamped on the lot

When to use this pattern

ScenarioApproach
Show pending rewards at authorization, reconcile at settlementAuth/settle pattern (this section)
Credit rewards only after settlementCredit on settlement directly — no holds needed. See Stripe Issuing.
Reserve existing points for a pending operation (e.g., redemption approval)Hold and release without reference_id

Lot Preservation

For LOT-mode assets, hold and release operations preserve lot metadata. When a lot moves from AVAILABLE to HELD (or back), its expires_at, matures_at, created_at, and lot ID all remain unchanged. The lot keeps its identity across bucket transitions.

Inactive Participants

Most balance operations (adjust, hold, release) are blocked for SUSPENDED and CLOSED participants. The API returns a 409 error with code participant_inactive. Forfeit and void hold are the exceptions: they are allowed on CLOSED participants so you can clean up remaining balances after account closure. Both are still blocked for SUSPENDED participants.