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.

A participant represents a user in your system. You identify participants with your own external IDs, and Scrip handles enrollment, balances, and state tracking.

Creating Participants

Explicitly via the API

curl -X POST https://api.scrip.dev/v1/participants \
  -H "Authorization: Bearer $SCRIP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "user_123",
    "program_id": "{program_id}",
    "email": "jane@example.com",
    "first_name": "Jane",
    "last_name": "Doe"
  }'
FieldRequiredDescription
external_idYesYour application’s user identifier
program_idNoEnroll the participant in a program on creation
statusNoACTIVE (default), SUSPENDED, or CLOSED
emailNoContact email (validated)
phoneNoContact phone number
first_nameNoFirst name
last_nameNoLast name
display_nameNoDisplay name
attributesNoKey-value metadata
tagsNoList of string labels
If a participant with the same external_id already exists, the call upserts: it updates the existing participant instead of creating a duplicate.

Automatically on first event

When a program’s on_unknown_participant is set to CREATE (the default), participants are created the first time you send an event with an unrecognized external_id:
curl -X POST https://api.scrip.dev/v1/events \
  -H "Authorization: Bearer $SCRIP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "program_id": "{program_id}",
    "external_id": "user_456",
    "idempotency_key": "signup-user-456",
    "event_timestamp": "2025-01-15T10:30:00Z",
    "event_data": {"type": "signup"}
  }'
Scrip creates the participant, enrolls them in the program, and processes the event in one step. Set on_unknown_participant to REJECT if you want to require explicit creation before sending events. See Programs for details.

Auto-enrollment of existing participants

When an event targets a participant who exists but isn’t enrolled in the event’s program, Scrip automatically enrolls them. This applies across all identity paths (external_id, participant_id, recipient_id, recipient_external_id). Inactive enrollments (FROZEN, LOCKED, or CLOSED) are reactivated. The on_unknown_participant setting only controls whether new participants are created. It does not affect enrollment of existing ones.

Profile Fields

Participants support optional profile fields for contact and display information: email, phone, first_name, last_name, and display_name. These are returned in all participant responses (create, update, get, list). Profile fields follow a three-state convention:
Value sentBehavior
Omitted (or null)Existing value is preserved
Non-empty stringField is set to the new value
Empty string ("")Field is cleared
The email field is validated on both create and update. Invalid addresses return a 400 error.
# Update profile fields
curl -X PATCH https://api.scrip.dev/v1/participants/{id} \
  -H "Authorization: Bearer $SCRIP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "jane@example.com",
    "display_name": "Jane Doe"
  }'
# Clear a field by sending an empty string
curl -X PATCH https://api.scrip.dev/v1/participants/{id} \
  -H "Authorization: Bearer $SCRIP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": ""
  }'

Identifiers

Participants have two IDs:
IDFormatUse
idUUIDScrip’s internal identifier, used in all API paths
external_idStringYour application’s user ID, used for lookups
To find a participant by your external ID:
GET /v1/participants?external_id=user_123
Use the returned id for all subsequent API calls. The list endpoint returns identifiers, status, and profile fields. To get the full participant state in one call, use the detail endpoint:
GET /v1/participants/{id}
The detail response includes balances, tags, counters, attributes, tiers, program_ids, and profile fields inline.

Status

A participant’s status controls what operations are allowed:
StatusDescription
ACTIVENormal operation. All actions allowed.
SUSPENDEDTemporary freeze (e.g., fraud review, compliance hold). Reversible
CLOSEDDeactivated. Can be transitioned back to ACTIVE or SUSPENDED

What’s allowed by status

ActionACTIVESUSPENDED / CLOSED
CREDITYesBlocked
DEBITYesBlocked
HOLDYesBlocked
RELEASEYesBlocked
FORFEITYesBlocked
COUNTERYesBlocked
TAG / UNTAGYesAllowed
SET_ATTRIBUTEYesAllowed
SET_TIERYesAllowed
BROADCASTYesAllowed
SCHEDULE_EVENTYesAllowed
Balance and counter actions are blocked because a suspended or closed participant should not accumulate value. Metadata actions (tags, attributes, tiers) are allowed because you still need to manage inactive accounts: tagging a participant with fraud_confirmed, setting attributes for audit trails, or adjusting tiers during a review period.

How this affects events

Events targeting suspended or closed participants are still accepted (202) and processed normally. The outcome depends on which actions the matching rules trigger:
  • If a rule triggers a blocked action (e.g., CREDIT), the action fails and the event is marked FAILED.
  • If matching rules only trigger allowed actions (e.g., TAG, SET_ATTRIBUTE), those actions execute and the event is marked COMPLETED.
  • If no rules match, the event is marked COMPLETED with no actions taken.
If you need events to fail unconditionally for inactive participants, add a CEL guard condition to your rules: participant.status == "ACTIVE". This causes the rule not to match, so no actions execute and the event completes with no effect.
PATCH /v1/participants/{id}/status
{"status": "SUSPENDED"}

Balances

Check a participant’s current balances across all assets:
GET /v1/participants/{id}/balances
Balances are split by asset into three buckets:
BucketDescription
AVAILABLESpendable immediately
HELDReserved for authorization holds, pending settlements, or fraud review
DEFERREDNot yet matured. Credits with a future matures_at land here and move to AVAILABLE automatically when the date passes.
You can perform manual balance adjustments directly on a participant:
POST /v1/participants/{id}/balances/adjust
{
  "program_id": "{program_id}",
  "asset_id": "{asset_id}",
  "type": "CREDIT",
  "amount": "500",
  "description": "Customer service goodwill credit"
}
See Balance Operations for hold, release, forfeit, and other operations.

Transaction History

View the ledger entries for a participant:
GET /v1/participants/{id}/activity/history
Returns a chronological list of credits, debits, holds, and releases with journal entry details.

Time-Range Filters

The endpoint supports two independent time-range filters that can be combined (AND semantics):
FilterFilters onUse case
from / tocreated_at (system ingestion time)When the journal entry was recorded
event_from / event_toevent_timestamp (event occurrence time)When the originating event occurred in your system
Entries without an originating event fall back to created_at for event_from/event_to filtering. You can also retrieve the events processed for a participant:
GET /v1/participants/{id}/activity/events

Participant State

Each participant carries state that rules can read and update:
TypePurposeExample
TagsBoolean flagsvip, first_purchase
CountersNumeric trackerspurchase_count: 42
AttributesKey-value stringsregion: "US"
TiersStatus levels with benefitsloyalty_tier: "gold"
Tags are normalized to lowercase. Counters support high-precision numerics. See State Management for how to read, update, and use state in rules.