Skip to main content
A program is the top-level container in Scrip. It holds your rules, links to your assets, and scopes your participants. You might have one program for your entire rewards system, or separate programs for distinct campaigns like “Q4 Referral Bonus” or “Employee Recognition.” Programs provide isolation. Rules in one program never trigger on events sent to another, so you can run multiple campaigns in parallel without rule conflicts.

Creating a Program

A program only requires a name. You can set the enrollment policy and other options at creation or update them later.
curl -X POST https://api.scrip.dev/v1/programs \
  -H "Authorization: Bearer $SCRIP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Customer Loyalty",
    "description": "Main loyalty track for retail customers"
  }'
FieldRequiredDescription
nameYesDisplay name (1-255 characters)
descriptionNoAdditional context (max 1000 characters)
on_unknown_participantNoCREATE (default) or REJECT. See Enrollment below.
The response returns a program_id that you’ll use when sending events, creating rules, and querying balances.

The Program Wallet

Every program has a wallet. How it’s used depends on the asset’s issuance policy:
  • UNLIMITED credits mint new value directly. The wallet is bypassed. This is what most programs use.
  • PREFUNDED credits draw from the wallet. You fund it up front, and every credit to a participant debits the wallet. When the wallet is empty, credits fail. This is how you enforce a fixed budget.
If you’re using PREFUNDED assets, you manage the wallet with fund and burn:
# Fund the wallet
POST /v1/programs/{id}/assets/{assetId}/fund
{"amount": "100000", "description": "Q1 budget allocation"}

# Burn unused funds
POST /v1/programs/{id}/assets/{assetId}/burn
{"amount": "5000", "description": "Reduce remaining Q1 budget"}

Linking Assets

Assets exist at the organization level. To use an asset in a program, it must be linked. When you create a new asset with a program_id, the link happens automatically. To share an existing asset across programs, link it manually:
POST /v1/programs/{programId}/assets
{"asset_id": "{asset_id}"}
This is how you create a shared economy, where participants earn the same asset across multiple programs.
An asset cannot be unlinked from a program if any ledger entries exist for that asset in that program.

Program State

Programs support the same state types as participants: tags, counters, and attributes. Use them for global logic that isn’t tied to any one participant. For example, a “first 1,000 signups” cap using a program-level counter:
{
  "name": "Early Adopter Bonus",
  "condition": "event.type == 'signup' && get(program.counters, 'total_signups', 0.0) < 1000.0",
  "actions": [
    {"type": "CREDIT", "asset_id": "...", "amount": "100"},
    {"type": "COUNTER", "key": "total_signups", "value": "1", "target": {"type": "PROGRAM"}}
  ]
}
See State Management for more on program-level state.

Program Status

You can pause or retire a program by updating its status:
StatusBehavior
ACTIVEEvents process, rules evaluate normally
SUSPENDEDNew events are rejected. Useful for pausing a program while investigating an issue. Can be reactivated.
ARCHIVEDHidden from default listings. Historical data remains for auditing.
PATCH /v1/programs/{id}
{"status": "SUSPENDED"}

Enrollment

Existing participants are automatically enrolled in a program the first time an event targets them in that program. This applies across all identity paths and requires no configuration. Inactive enrollments (FROZEN, LOCKED, or CLOSED) are reactivated. The on_unknown_participant setting controls what happens when an event arrives for a participant that doesn’t exist yet:
SettingBehavior
CREATE (default)Scrip creates the participant automatically, enrolls them, and processes the event. No separate registration step needed.
REJECTThe event fails. You must create participants explicitly via POST /v1/participants before sending events for them. Existing participants are still auto-enrolled.
Most programs use CREATE for simplicity. Use REJECT when you need strict control over who can participate.

Automations

Programs can have automations that generate events on a schedule, at a specific time, or in response to participant state changes. Automations are scoped to a program and managed via POST /v1/programs/{id}/automations. See Automations for details.