Skip to main content
Scrip is built around six concepts: programs, assets, participants, events, rules, and a double-entry ledger. The table below summarizes each one, and the end-to-end example at the bottom shows how they connect.
ConceptWhat it isGuide
ProgramContainer for rules, assets, and participantsPrograms
AssetUnit of value: points, cashback, nights, creditsAssets
ParticipantA user in your system, identified by your external_idParticipants
EventA signal from your app that triggers rule evaluationEvents
RuleA CEL condition and a list of actionsRules
LedgerDouble-entry record of every balance changeLedger

Programs

A program is the top-level container. It holds your rules, links to your assets, and scopes your participants. Most teams create one program per use case: “Customer Loyalty,” “Referral Rewards,” “Driver Incentives.” Events are always sent to a specific program, and rules belong to exactly one program.

Assets

An asset is the unit of value you’re tracking. Points, cashback dollars, nights stayed, referral credits. You configure how each asset behaves:
  • Inventory mode: SIMPLE tracks a single aggregate balance. LOT tracks each credit individually with its own expiration, maturity, issuer lineage, and oldest-first spending.
  • Issuance policy: UNLIMITED mints new value on every credit (no cap). PREFUNDED draws from a fixed program wallet, so credits fail when the wallet is empty. Use PREFUNDED when you need to enforce a budget.

Participants

Participants are your users. You identify them with your own external_id so they stay in sync with your application. Participants can be created explicitly or automatically on first event. Each participant carries state that rules can read and write:
  • Tags: Boolean flags like vip or first_purchase.
  • Counters: Numeric values like lifetime_spend or purchase_count.
  • Attributes: Key-value strings like region: "US".

Events

Events are the inputs. Your application sends one whenever something happens that might affect a participant: a purchase, a signup, a referral, a cancellation.
{
  "program_id": "...",
  "external_id": "user_123",
  "idempotency_key": "order-456-completed",
  "event_timestamp": "2025-01-15T10:30:00Z",
  "event_data": {
    "type": "purchase",
    "amount": 105.00
  }
}
Events process asynchronously. The API confirms receipt and a worker evaluates rules in the background. The idempotency_key ensures exactly-once event processing: if you retry with the same key, you get back the same event identity instead of processing it again. To correct or replace an event, send a new event with a new idempotency key.

Rules

A rule is a condition and a list of actions. The condition is a CEL expression (Common Expression Language, a lightweight expression syntax) evaluated against the event and the participant’s current state. When it returns true, the actions fire.
{
  "name": "Cashback on large purchases",
  "condition": "event.type == 'purchase' && event.amount >= 100.0",
  "actions": [
    {"type": "CREDIT", "asset_id": "...", "amount": "10"}
  ]
}
Rules evaluate in order. Multiple rules can fire on the same event. If you want only the first matching rule to fire, set stop_after_match: true on that rule to skip the remaining rules.

The Ledger

Every balance change is recorded as a journal entry with two sides: a debit and a credit. When a participant earns points, the credit goes to the participant and the corresponding debit comes from a source account: the system issuance account for UNLIMITED assets, or the program wallet for PREFUNDED. Nothing is ever mutated in place. This gives you a complete audit trail: you can trace any balance back to the event and rule that created it.

End-to-End Example

Using the “Cashback on large purchases” rule from above, here’s what happens when a $105 purchase comes in:
1

Your app sends an event

user_123 made a $105 purchase. You send it to Scrip with external_id: "user_123" and event_data: {type: "purchase", amount: 105.00}.
2

The engine loads the participant

Scrip looks up the participant matching user_123 and loads their current state: tags, counters, and attributes.
3

Rules evaluate in order

The condition event.type == 'purchase' && event.amount >= 100.0 is checked. 105 >= 100, so it matches.
4

The CREDIT action fires

$10 is credited to the participant’s balance.
5

The ledger records it

A journal entry credits user_123 by $10 and debits the source account by $10 (the system issuance account for an UNLIMITED asset, or the program wallet for PREFUNDED).
6

Result

user_123 now has $10.00 available to redeem.
The Quickstart walks through this exact flow with real API calls.