curl --request POST \
--url https://api.scrip.dev/v1/events \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"event_data": {},
"event_timestamp": "2024-01-15T10:30:00Z",
"idempotency_key": "order-12345-completed",
"program_id": "550e8400-e29b-41d4-a716-446655440000",
"external_id": "user_abc123",
"participant_id": "550e8400-e29b-41d4-a716-446655440000",
"recipient_external_id": "user_xyz789",
"recipient_id": "550e8400-e29b-41d4-a716-446655440001"
}
'import requests
url = "https://api.scrip.dev/v1/events"
payload = {
"event_data": {},
"event_timestamp": "2024-01-15T10:30:00Z",
"idempotency_key": "order-12345-completed",
"program_id": "550e8400-e29b-41d4-a716-446655440000",
"external_id": "user_abc123",
"participant_id": "550e8400-e29b-41d4-a716-446655440000",
"recipient_external_id": "user_xyz789",
"recipient_id": "550e8400-e29b-41d4-a716-446655440001"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
event_data: {},
event_timestamp: '2024-01-15T10:30:00Z',
idempotency_key: 'order-12345-completed',
program_id: '550e8400-e29b-41d4-a716-446655440000',
external_id: 'user_abc123',
participant_id: '550e8400-e29b-41d4-a716-446655440000',
recipient_external_id: 'user_xyz789',
recipient_id: '550e8400-e29b-41d4-a716-446655440001'
})
};
fetch('https://api.scrip.dev/v1/events', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.scrip.dev/v1/events",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'event_data' => [
],
'event_timestamp' => '2024-01-15T10:30:00Z',
'idempotency_key' => 'order-12345-completed',
'program_id' => '550e8400-e29b-41d4-a716-446655440000',
'external_id' => 'user_abc123',
'participant_id' => '550e8400-e29b-41d4-a716-446655440000',
'recipient_external_id' => 'user_xyz789',
'recipient_id' => '550e8400-e29b-41d4-a716-446655440001'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.scrip.dev/v1/events"
payload := strings.NewReader("{\n \"event_data\": {},\n \"event_timestamp\": \"2024-01-15T10:30:00Z\",\n \"idempotency_key\": \"order-12345-completed\",\n \"program_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"external_id\": \"user_abc123\",\n \"participant_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"recipient_external_id\": \"user_xyz789\",\n \"recipient_id\": \"550e8400-e29b-41d4-a716-446655440001\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.scrip.dev/v1/events")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"event_data\": {},\n \"event_timestamp\": \"2024-01-15T10:30:00Z\",\n \"idempotency_key\": \"order-12345-completed\",\n \"program_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"external_id\": \"user_abc123\",\n \"participant_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"recipient_external_id\": \"user_xyz789\",\n \"recipient_id\": \"550e8400-e29b-41d4-a716-446655440001\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.scrip.dev/v1/events")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"event_data\": {},\n \"event_timestamp\": \"2024-01-15T10:30:00Z\",\n \"idempotency_key\": \"order-12345-completed\",\n \"program_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"external_id\": \"user_abc123\",\n \"participant_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"recipient_external_id\": \"user_xyz789\",\n \"recipient_id\": \"550e8400-e29b-41d4-a716-446655440001\"\n}"
response = http.request(request)
puts response.read_body{
"event_timestamp": "2024-01-15T10:30:00Z",
"external_id": "user_abc123",
"id": "550e8400-e29b-41d4-a716-446655440000",
"idempotency_key": "order-12345-completed",
"participant_id": "550e8400-e29b-41d4-a716-446655440002",
"program_id": "550e8400-e29b-41d4-a716-446655440001",
"status": "PENDING"
}{
"code": "bad_request",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Invalid request parameters"
}{
"code": "unauthorized",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Missing or invalid credentials"
}{
"code": "forbidden",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Insufficient permissions for this action"
}{
"code": "unsupported_media_type",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Content-Type must be application/json"
}{
"code": "internal_error",
"message": "An internal error occurred"
}Ingest an event
Submit an event for asynchronous rule evaluation. Resubmitting the same idempotency_key for a program returns the existing event instead of creating a new one.
curl --request POST \
--url https://api.scrip.dev/v1/events \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"event_data": {},
"event_timestamp": "2024-01-15T10:30:00Z",
"idempotency_key": "order-12345-completed",
"program_id": "550e8400-e29b-41d4-a716-446655440000",
"external_id": "user_abc123",
"participant_id": "550e8400-e29b-41d4-a716-446655440000",
"recipient_external_id": "user_xyz789",
"recipient_id": "550e8400-e29b-41d4-a716-446655440001"
}
'import requests
url = "https://api.scrip.dev/v1/events"
payload = {
"event_data": {},
"event_timestamp": "2024-01-15T10:30:00Z",
"idempotency_key": "order-12345-completed",
"program_id": "550e8400-e29b-41d4-a716-446655440000",
"external_id": "user_abc123",
"participant_id": "550e8400-e29b-41d4-a716-446655440000",
"recipient_external_id": "user_xyz789",
"recipient_id": "550e8400-e29b-41d4-a716-446655440001"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
event_data: {},
event_timestamp: '2024-01-15T10:30:00Z',
idempotency_key: 'order-12345-completed',
program_id: '550e8400-e29b-41d4-a716-446655440000',
external_id: 'user_abc123',
participant_id: '550e8400-e29b-41d4-a716-446655440000',
recipient_external_id: 'user_xyz789',
recipient_id: '550e8400-e29b-41d4-a716-446655440001'
})
};
fetch('https://api.scrip.dev/v1/events', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.scrip.dev/v1/events",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'event_data' => [
],
'event_timestamp' => '2024-01-15T10:30:00Z',
'idempotency_key' => 'order-12345-completed',
'program_id' => '550e8400-e29b-41d4-a716-446655440000',
'external_id' => 'user_abc123',
'participant_id' => '550e8400-e29b-41d4-a716-446655440000',
'recipient_external_id' => 'user_xyz789',
'recipient_id' => '550e8400-e29b-41d4-a716-446655440001'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.scrip.dev/v1/events"
payload := strings.NewReader("{\n \"event_data\": {},\n \"event_timestamp\": \"2024-01-15T10:30:00Z\",\n \"idempotency_key\": \"order-12345-completed\",\n \"program_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"external_id\": \"user_abc123\",\n \"participant_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"recipient_external_id\": \"user_xyz789\",\n \"recipient_id\": \"550e8400-e29b-41d4-a716-446655440001\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.scrip.dev/v1/events")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"event_data\": {},\n \"event_timestamp\": \"2024-01-15T10:30:00Z\",\n \"idempotency_key\": \"order-12345-completed\",\n \"program_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"external_id\": \"user_abc123\",\n \"participant_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"recipient_external_id\": \"user_xyz789\",\n \"recipient_id\": \"550e8400-e29b-41d4-a716-446655440001\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.scrip.dev/v1/events")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"event_data\": {},\n \"event_timestamp\": \"2024-01-15T10:30:00Z\",\n \"idempotency_key\": \"order-12345-completed\",\n \"program_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"external_id\": \"user_abc123\",\n \"participant_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"recipient_external_id\": \"user_xyz789\",\n \"recipient_id\": \"550e8400-e29b-41d4-a716-446655440001\"\n}"
response = http.request(request)
puts response.read_body{
"event_timestamp": "2024-01-15T10:30:00Z",
"external_id": "user_abc123",
"id": "550e8400-e29b-41d4-a716-446655440000",
"idempotency_key": "order-12345-completed",
"participant_id": "550e8400-e29b-41d4-a716-446655440002",
"program_id": "550e8400-e29b-41d4-a716-446655440001",
"status": "PENDING"
}{
"code": "bad_request",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Invalid request parameters"
}{
"code": "unauthorized",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Missing or invalid credentials"
}{
"code": "forbidden",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Insufficient permissions for this action"
}{
"code": "unsupported_media_type",
"details": {
"expected": "<string>",
"field": "<string>",
"fields": [
{
"expected": "<unknown>",
"field": "<string>",
"message": "This field is required",
"reason": "required",
"received": "<string>"
}
],
"reason": "invalid",
"received": "<string>"
},
"message": "Content-Type must be application/json"
}{
"code": "internal_error",
"message": "An internal error occurred"
}202 Accepted immediately. A worker picks up the event, evaluates applicable ACTIVE rules across every rule set in set and rule order, and executes matching actions.
Identify the participant with exactly one of external_id or participant_id. Pass event_timestamp for when the event occurred and event_data containing the payload your rules will evaluate against. Optionally set recipient_id or recipient_external_id to route rewards to a different participant (e.g. gifting).
The idempotency_key is required and scoped per program. Submitting the same key again returns the same event identity without reprocessing, even if the payload differs. To correct or replace an event, submit a new event with a new idempotency key. Use deterministic keys like order-12345-completed, not random UUIDs.
If the participant doesn’t exist yet and the program’s on_unknown_participant is CREATE, Scrip creates the participant and processes the event in one step. The on_unknown_participant setting controls creation of new participants only.
Existing participants are automatically enrolled in the target program if not already members. An inactive enrollment is reactivated automatically. Enrollment behavior applies regardless of the on_unknown_participant setting.
Business validation (program existence and status, participant resolution) happens asynchronously. A 202 Accepted response confirms receipt, not that the event is valid or will complete. Subscribe to event.failed webhooks for error notification. Failed events expose a machine-readable error_code (when the failure has a classified code, such as participant_suspended or program_inactive) on both the event resource and the event.failed webhook payload.
Read-after-write is not immediate: the returned id may briefly 404 on GET /v1/events/{id} and GET /v1/events/by-key (typically well under a second) until Scrip finishes recording the event. The ID is durable; poll until it resolves. Every accepted submission eventually becomes readable, either as a processed event or as status FAILED with an error_code if it was rejected asynchronously.
Events whose resolved actor or recipient is SUSPENDED or CLOSED are rejected before any rule runs: the event is accepted and then recorded as a terminal FAILED event with code participant_suspended or participant_closed.
Numeric values in event_data whose magnitude exceeds 2^53 are rejected with 400 amount_precision_exceeded; send very large values as strings to preserve them exactly.
Authorizations
API key passed in the X-API-Key header.
Body
Event with event_data for rule evaluation
JSON object exposed to rule conditions and action expressions as event data
Time the event occurred; rules evaluate against this timestamp rather than ingestion time
"2024-01-15T10:30:00Z"
Client-generated key that deduplicates event submission within the program
1 - 255"order-12345-completed"
Program that supplies the rules evaluated for this event
"550e8400-e29b-41d4-a716-446655440000"
Your system's identifier for the user who triggered this event. Mutually exclusive with participant_id - exactly one must be provided. Auto-creates a participant if this ID doesn't exist (based on the program's on_unknown_participant setting). Existing participants are automatically enrolled in the target program.
1 - 255"user_abc123"
Scrip's UUID for the participant. Mutually exclusive with external_id - exactly one must be provided. The participant is automatically enrolled in the target program if not already a member.
"550e8400-e29b-41d4-a716-446655440000"
Optional external identifier for a second participant the event is for, such as a gift recipient. Rules read it as recipient.id and recipient.external_id. It does not move rewards by itself: actions without a target still apply to the sender, so pay the recipient with a target such as {"participant_id": "${{ recipient.id }}"}. Mutually exclusive with recipient_id. Auto-creates the recipient if this ID doesn't exist (based on the program's on_unknown_participant setting), mirroring external_id. An existing recipient is automatically enrolled in the target program if not already a member.
1 - 255"user_xyz789"
Optional Scrip UUID for a second participant the event is for, such as a gift recipient. Rules read it as recipient.id and recipient.external_id. It does not move rewards by itself: actions without a target still apply to the sender, so pay the recipient with a target such as {"participant_id": "${{ recipient.id }}"}. Mutually exclusive with recipient_external_id. Must reference an existing participant; they are automatically enrolled in the target program if not already a member.
"550e8400-e29b-41d4-a716-446655440001"
Response
Event accepted for async processing (new or duplicate)
When the event occurred (from the ingestion request)
"2024-01-15T10:30:00Z"
Your system's identifier for the user, if provided
"user_abc123"
Unique identifier for the created event
"550e8400-e29b-41d4-a716-446655440000"
Client-provided unique key for deduplication
"order-12345-completed"
Participant UUID. May be null if only external_id was provided (resolution may be deferred to async processing).
"550e8400-e29b-41d4-a716-446655440002"
Program the event was ingested into
"550e8400-e29b-41d4-a716-446655440001"
Processing status (PENDING on initial ingestion)
"PENDING"