Skip to content

Events API

Events are the primary data ingestion point for Synapse. Your application sends events when users take actions (sign up, purchase, page view), and Synapse uses these events to trigger flows, update contact profiles, and drive analytics.

Note

For a platform guide on events, see Events. For authentication details, see Authentication.


Authentication

Events API uses API key authentication with the data scope.

bash
curl -X POST https://synapse-api.pyrx.tech/v1/events \
-H "Content-Type: application/json" \
-H "X-WORKSPACE-ID: ws_k7x9m2p4" \
-H "X-API-KEY: psk_live_a1b2c3d4e5f67890abcdef1234567890"

Endpoints

MethodPathDescription
POST/v1/eventsIngest a single event
POST/v1/events/batchIngest up to 50 events in one request
GET/v1/eventsList events (JWT auth, dashboard)
GET/v1/events/{id}Get event details (JWT auth, dashboard)
POST/v1/events/triggerTrigger a test event (JWT auth, dashboard)
POST/v1/events/{id}/retriggerRe-trigger flow processing for an event (JWT auth, dashboard)

Ingest Event

POST /v1/events

Auth: API key (data scope)

Request Body

json
{
"event_name": "purchase_completed",
"external_id": "user_12345",
"attributes": {
"order_id": "ord_abc123",
"amount": 99.99,
"currency": "USD",
"items": ["widget-pro", "widget-basic"]
},
"contact_overrides": {
"email": "[email protected]",
"first_name": "Jane"
},
"idempotency_key": "purchase_ord_abc123",
"timestamp": "2026-05-06T10:15:30Z"
}
FieldTypeRequiredDescription
event_namestringYesSnake-case event name (e.g., purchase_completed)
external_idstringYesYour application's user ID for the contact
attributesobjectNoEvent-specific data (used in templates and conditions)
contact_overridesobjectNoUpdate contact fields (email, first_name, last_name, phone, properties)
idempotency_keystringNoPrevents duplicate processing (7-day TTL)
timestampstringNoISO 8601 timestamp (defaults to server time)

Response (202 Accepted)

json
{
"event_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"status": "accepted"
}
Tip

Events are processed asynchronously. A 202 Accepted response means the event has been queued for processing, not that flows have been triggered yet.


Batch Ingest

POST /v1/events/batch

Auth: API key (data scope)

Send up to 50 events in a single request. Each accepted event counts as 1 event against your monthly limit, but the entire batch counts as 1 request against the rate limit.

The batch endpoint is partial-accept: each event is validated and ingested independently, so one bad event no longer fails the whole batch. Valid events land even when a sibling event is malformed or over the plan limit.

Request Body

json
{
"events": [
{
"event_name": "page_viewed",
"external_id": "user_123",
"attributes": {"page": "/pricing"}
},
{
"event_name": "page_viewed",
"external_id": "user_456",
"attributes": {"page": "/docs"}
}
]
}

Each event follows the same schema as POST /v1/events. The events array must contain 1 to 50 items. A batch that is malformed as a whole — missing the events key, events is not an array, or more than 50 items — is rejected with 422 at the request boundary before any event is processed.

Response header opt-in

Send the X-Synapse-SDK-Version: 2 request header to receive the structured, per-event partial-accept response body. Clients that omit the header get the legacy response shape (see Legacy clients below). The Browser SDK sends this header automatically from version 0.5.0.

Response (202 Accepted) — with X-Synapse-SDK-Version: 2

Returned when at least one event was accepted. rejected is a list of the events that did not land, each tagged with the original 0-based index in your events array and a machine-readable reason.

json
{
"accepted": 2,
"rejected": [
{
"index": 3,
"reason": "invalid_schema",
"detail": "1 validation error for EventIngest\n Value error, Either 'external_id' or 'user_id' must be provided"
},
{ "index": 4, "reason": "over_limit", "detail": null }
]
}
FieldTypeDescription
acceptedintegerNumber of events accepted and queued for processing
rejectedarrayOne entry per event that was not accepted (empty array when all accepted)
rejected[].indexinteger0-based position of the event in the submitted events array
rejected[].reasonstringRejection reason — one of invalid_schema, over_limit, runtime_error
rejected[].detailstring | nullHuman-readable detail for invalid_schema — the raw validation message (multi-line, may be verbose); null for over_limit. Treat it as a diagnostic aid to log, not a stable string to parse.

Rejection reasons:

ReasonMeaningShould you retry?
invalid_schemaThe event failed validation (missing required field, bad type).No — it can never succeed. Fix your instrumentation.
over_limitThe event is valid, but your workspace's monthly events quota was reached before this event could be counted.Yes, later — retry after your quota resets or you upgrade.
runtime_errorAn unexpected server-side error occurred while ingesting this specific event.Yes — safe to retry.

Status matrix

OutcomeStatusBody
At least one event accepted202 AcceptedPartial-accept body (accepted count + any rejections)
Zero accepted, all rejections are invalid_schema422 Unprocessable ContentPartial-accept body
Zero accepted, any rejection is over_limit403 Forbiddenplan_limit_reached error (see below)

When every event in a batch is over the plan limit (nothing accepted), the response is a 403 with the standard plan-limit error shape:

json
{
"detail": "monthly_events limit reached (250000/250000)",
"code": "plan_limit_reached",
"limit_type": "monthly_events",
"current": 250000,
"maximum": 250000,
"plan": "starter"
}

Legacy clients (no version header)

Clients that do not send X-Synapse-SDK-Version receive the original response shape, where rejected is a count — byte-identical to the pre-partial-accept response:

json
{
"accepted": 2,
"rejected": 1
}
Warning

Plan-limit behavior differs for legacy clients. A legacy client whose batch would partially exceed the monthly events quota receives a 403 plan_limit_reached for the whole batch — rather than a 202 that silently drops the over-limit events. This is deliberate: an older client that re-queues on 4xx will retry the whole batch later instead of losing data. Send X-Synapse-SDK-Version: 2 to instead get a 202 with the accepted events plus over_limit entries you re-queue yourself.

Tip

Events are queued for asynchronous processing. A 202 Accepted (or the accepted count) means the event was queued, not that flows have been triggered yet.


List Events (Dashboard)

GET /v1/events

Auth: JWT (dashboard). Requires events:read permission.

Query Parameters

ParameterTypeDefaultDescription
searchstringSearch by event name or external ID
event_namestringFilter by event name
limitinteger50Max items per page (max 100)
offsetinteger0Pagination offset

Plan Limits

PlanMonthly Events
Free10,000
Starter100,000
Growth1,000,000
EnterpriseUnlimited

Exceeding the monthly event limit returns 403 with code plan_limit_reached.