---
title: "Ingest an event"
method: POST
path: "/v1/events"
tags: ["Events"]
---

# Ingest an event

`POST /v1/events`

## Request

- Base URL: `https://api.scrip.dev`
- URL: `https://api.scrip.dev/v1/events`
- Auth: HTTP bearer

Submit an event for asynchronous rule evaluation; the same `idempotency_key` for a program returns the existing event instead of creating a new one.

## Headers

- `Idempotency-Key` string

## Request body

- EventRequest: An event to ingest. Rules in the program evaluate it to decide what rewards to issue.
  - `event_data` object, required: JSON object exposed to rule conditions and action expressions as event data
  - `event_timestamp` string, date-time, required: Time the event occurred; rules evaluate against this timestamp rather than ingestion time
  - `external_id` string: 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.
  - `idempotency_key` string, required: Your key for this event within the program. Send it here or in the Idempotency-Key header. Sending the same key again returns the same event.
  - `participant_id` string, uuid: 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.
  - `program_id` string, uuid, required: Program that supplies the rules evaluated for this event
  - `recipient_external_id` string: 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.
  - `recipient_id` string, uuid: 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.

## Response `202`

Event accepted for async processing (new or duplicate)

- IngestionResponse: An event that Scrip accepted, with its id and where to read its progress.
  - `event_timestamp` string, date-time: When the event occurred (from the ingestion request)
  - `external_id` string: Your system's identifier for the user, if provided
  - `id` string, uuid: Unique identifier for the created event
  - `idempotency_key` string: Client-provided unique key for deduplication
  - `participant_id` string, uuid: Participant UUID. May be null if only external_id was provided (resolution may be deferred to async processing).
  - `program_id` string, uuid: Program the event was ingested into
  - `status` 'PENDING' | 'PROCESSING' | 'COMPLETED' | 'FAILED': The event's processing status right after it is accepted.
  - `status_url` string: Path to fetch this event and its processing status, such as `/v1/events/{id}`; the same path is in the `Location` header of the single-event response

## Other responses

- `400`: Validation failed (code: validation_error | invalid_json)
- `401`: The API key is missing or not valid. See [Authentication](https://docs.scrip.dev/authentication).
- `403`: The caller is not allowed to do this.
- `429`: Too many requests. Wait the number of seconds in the `Retry-After` header, then retry. See [Limits and durations](https://docs.scrip.dev/limits#rate-limits).
- `500`: Something failed on Scrip's side. Retry the request, and quote `request_id` if it keeps failing. [Errors](https://docs.scrip.dev/errors) lists every code, and the [API introduction](https://docs.scrip.dev/api-reference/introduction) describes the error format.

---

[API](https://skmtc.dev/scrip/apis/scrip-api.md) · [All operations](https://skmtc.dev/scrip/apis/scrip-api/llms.txt) · [OpenAPI document](https://skmtc.dev/scrip/apis/scrip-api/revisions/c52d35da8d97?raw)
