Skip to main content
The Scrip API is a REST API. All requests and responses use JSON. The current version is v1.
https://api.scrip.dev/v1
New to Scrip? Start with the Quickstart to create a program and process your first event, or read Core Concepts for an overview of the data model.

Authentication

Every request requires an API key in the Authorization header:
curl https://api.scrip.dev/v1/programs \
  -H "Authorization: Bearer sk_your_api_key" \
  -H "Content-Type: application/json"
Keys use the sk_ prefix and have full read/write access to all resources in your organization. You can also pass the key via the X-API-Key header. Create and manage keys from the Scrip dashboard. See the Authentication page for details on key management and rate limits.

Conventions

ConventionDetail
Property namessnake_case
IDsUUIDs (550e8400-e29b-41d4-a716-446655440000)
TimestampsRFC 3339 (2024-01-15T10:30:00Z)
AmountsStrings to preserve decimal precision ("100.00")
Content typeapplication/json for all request and response bodies

Idempotency

Events, balance operations (hold, release, forfeit), redemptions, reversals, and transfers accept an idempotency_key field. Idempotency behavior differs by resource type:
  • Balance operations, redemptions, reversals, and transfers: Resubmitting with the same key and same payload returns the original response (200) without reprocessing. Resubmitting with the same key but a different payload returns 409 Conflict with code idempotency_conflict. Payload comparison uses a SHA-256 hash of the request’s semantic fields — cosmetic differences (JSON key order, trailing decimal zeros, whitespace) are normalized.
  • Events: Duplicate keys are silently deduplicated. The original event takes precedence and subsequent submissions with the same program_id + idempotency_key are ignored, regardless of payload differences. If the payload needs to change, use a new key.
Use deterministic keys like order-12345-completed, not random UUIDs.

Pagination

List endpoints return paginated results using cursor-based pagination:
{
  "data": [
    { "id": "...", "name": "..." }
  ],
  "pagination": {
    "has_more": true,
    "next_cursor": "eyJ..."
  }
}
Pass cursor as a query parameter to fetch the next page. Use limit to control page size (default 50, max 200). Pagination is stable across concurrent modifications — inserts and deletes between pages do not cause skipped or duplicated results.
Most list endpoints support query parameters for narrowing and ordering results. The exact parameters vary by endpoint — check each endpoint’s parameter table for available options.

Filtering

Filter by resource status or related IDs:
GET /v1/participants?status=ACTIVE&program_id=550e8400-...
GET /v1/events?status=COMPLETED&from=2025-01-01T00:00:00Z&to=2025-02-01T00:00:00Z
Time-range filters (from, to) accept RFC 3339 timestamps and filter on the resource’s creation time. from is required when to is provided, and from must be before to.

Sorting

Control result ordering with sort_by and sort_dir:
GET /v1/programs?sort_by=name&sort_dir=asc
GET /v1/events?sort_by=event_timestamp&sort_dir=desc
Each endpoint defines its own set of sortable fields (e.g., created_at, name, order). The default sort is typically created_at descending. Rules default to order ascending. Text search uses partial matching (case-insensitive) on the resource’s primary identifier:
GET /v1/participants?search=user_abc
GET /v1/rules?search=welcome
The search field varies by resource. Participants search by external_id, rules and programs search by name, and assets search by both symbol and name.

Errors

Error responses include a machine-readable code and a human-readable message:
{
  "code": "not_found",
  "message": "Program not found"
}
Some errors include a details object with field-level validation information:
{
  "code": "validation_error",
  "message": "Invalid request body",
  "details": {
    "name": "is required",
    "key": "must be lowercase alphanumeric with hyphens"
  }
}

Status codes

CodeMeaning
200Success
201Resource created
204Success, no content
400Bad request or validation error
401Unauthorized
403Forbidden
404Resource not found
409Conflict (state violation, duplicate key, over-reversal)
422Unprocessable entity (business rule violation)
429Rate limit exceeded
500Internal server error

Error codes

The code field in error responses is a machine-readable string you can match on programmatically. Common codes by category:

Validation (400)

CodeDescription
validation_errorOne or more fields failed validation. Check details for field-level errors
invalid_requestRequest structure is invalid (e.g., mutually exclusive fields provided)
invalid_amountAmount is malformed, non-positive, or exceeds asset scale
invalid_quantityQuantity must be a positive integer
asset_not_linkedAsset is not linked to the specified program
reward_program_mismatchReward does not belong to the specified program

Authentication & authorization (401 / 403)

CodeDescription
unauthorizedMissing or invalid API key / JWT
forbiddenValid credentials but insufficient permissions for this action

Not found (404)

CodeDescription
not_foundThe requested resource does not exist

Conflict (409)

CodeDescription
participant_inactiveParticipant is SUSPENDED or CLOSED. Financial operations and counter updates are blocked for inactive participants
idempotency_conflictIdempotency key was already used with different parameters
already_reversedRedemption has already been fully reversed
quantity_exceeds_remainingReversal quantity exceeds the remaining reversible units
amount_exceeds_remainingReversal amount exceeds the remaining reversible amount
max_total_exceededReward’s global inventory limit reached
max_per_participant_exceededReward’s per-participant inventory limit reached
program_archivedProgram is archived and cannot be modified
program_suspendedProgram is suspended and cannot process transactions
key_existsA tier key or reward name already exists in this program

Business rules (422)

CodeDescription
insufficient_fundsNot enough balance for the requested operation
asset_archivedAsset is archived and cannot be used in new operations
program_inactiveProgram is not active; events cannot be ingested
participant_not_foundParticipant does not exist and the program is configured to reject unknown participants
recipient_not_foundTarget participant not found or not enrolled in the program

Rate limits

Requests are rate-limited per organization at 10 requests/second with burst to 30. All API keys in the same organization share one rate limit bucket. Every response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. When exceeded, the API returns 429 with a Retry-After header. See Authentication for the full header reference.

Resources

ResourcePrefixDescription
Programs/v1/programsTop-level containers for incentive logic
Assets/v1/assetsUnits of value (points, credits, cashback)
Participants/v1/participantsUsers who earn and spend
Groups/v1/groupsCollections of participants
Rules/v1/rulesCEL conditions and reward actions
Events/v1/eventsSignals that trigger rule evaluation
Tiers/v1/programs/{id}/tiersStatus hierarchies for participants
Redemptions/v1/participants/{id}/redemptionsBalance spend operations
Rewards/v1/programs/{id}/rewardsCatalog items for redemption
Transfers/v1/transfersValue movement between participants
Automations/v1/programs/{id}/automationsScheduled event generation
Reporting/v1/reportsLedger summaries and activity
Logs/v1/logsRequest history and usage metrics
For a complete map of entities and their relationships, see the Data Model.