Skip to content

Event Ingestion

The event ingestion endpoint is the primary integration point between your application and Synapse. Events trigger flows, update contacts, and drive your communication pipeline.


Endpoint

POST https://synapse-api.pyrx.tech/v1/events

Headers

HeaderRequiredDescription
Content-TypeYesMust be application/json
X-WORKSPACE-IDYesYour workspace identifier
X-API-KEYYesA data-scoped API key

Request Body

json
{
"event_name": "order_completed",
"external_id": "user_12345",
"attributes": {
"order_id": "ORD-2026-0042",
"amount": 149.99,
"currency": "USD",
"items": [
{"name": "Widget Pro", "quantity": 2, "price": 49.99},
{"name": "Gadget X", "quantity": 1, "price": 50.01}
]
},
"contact": {
"email": "[email protected]",
"first_name": "Jane",
"last_name": "Doe",
"timezone": "Asia/Singapore",
"tags": ["active-buyer"],
"properties": {
"plan": "growth",
"company": "Acme Corp"
}
},
"idempotency_key": "order_completed_ORD-2026-0042"
}

Parameters

FieldTypeRequiredDescription
event_namestringYesThe event type identifier. Use snake_case (e.g., order_completed, claim_submitted).
external_idstringYesYour application's unique user identifier. Used to match or create a contact.
attributesobjectNoArbitrary JSON object containing event-specific data. Defaults to {}. Accessible in NLT templates via {the Amount from the trigger event}.
contactobjectNoFields to upsert on the contact record. See Contact Fields.
occurred_atdatetimeNoWhen the event occurred (ISO 8601, defaults to server time). Useful for historical imports.
idempotency_keystringNoA unique key to prevent duplicate processing (max 255 chars). See Idempotency.
Note

Backward compatibility: The fields user_id and contact_overrides are still accepted as deprecated aliases for external_id and contact respectively. New integrations should use external_id and contact.


Contact Fields

When provided, these fields are upserted on the contact matching external_id. If no contact exists, one is created.

FieldTypeDescription
emailstringThe contact's email address. Required for email delivery.
first_namestringFirst name. Used in NLT templates as {the user's First Name}.
last_namestringLast name.
phonestringPhone number.
timezonestringIANA timezone (e.g., Asia/Singapore). Used for send-time optimization.
localestringLocale code (e.g., en-SG, vi-VN). Used for localized content.
propertiesobjectCustom key-value pairs. Shallow-merged with existing properties.
tagsstring[]Replaces the contact's entire tag list. Use $add_tags/$remove_tags for surgical updates.
$add_tagsstring[]Appends tags to the existing list (duplicates ignored).
$remove_tagsstring[]Removes specified tags from the existing list.
Tip

Always include email in contact when sending events for new users. Without an email address, Synapse cannot deliver emails even if a flow matches.


Response

Success (202 Accepted)

json
{
"event_id": "evt_8f14e45f-ceea-467f-a83c-01a01ba3c5db",
"status": "accepted"
}

The event has been accepted and queued for asynchronous processing. Flow matching, trip creation, and email delivery happen in the background.

Error Responses

json
{
"detail": "Missing required field: event_name",
"code": "validation_error"
}
StatusCodeDescription
400validation_errorMissing or invalid required fields
401invalid_api_keyAPI key is missing, revoked, or invalid
403insufficient_scopeAPI key does not have data scope
403plan_limit_reachedMonthly events quota exceeded. Upgrade your plan or wait for the next billing cycle.
409duplicate_eventAn event with this idempotency_key was already processed
429rate_limit_exceededToo many requests -- see Rate Limits

Examples

curl

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" \
-d '{
"event_name": "claim_submitted",
"external_id": "worker_67890",
"attributes": {
"reference_number": "WIC-2026-0001",
"worker_name": "John Doe",
"incident_date": "2026-03-15T14:30:00+08:00",
"claim_type": "work_injury"
},
"contact": {
"email": "[email protected]",
"first_name": "John",
"timezone": "Asia/Ho_Chi_Minh",
"$add_tags": ["claimant"]
},
"idempotency_key": "claim_submitted_WIC-2026-0001"
}'

Python

python
import requests
 
response = requests.post(
"https://synapse-api.pyrx.tech/v1/events",
headers={
"X-WORKSPACE-ID": "ws_k7x9m2p4",
"X-API-KEY": "psk_live_a1b2c3d4e5f67890abcdef1234567890",
},
json={
"event_name": "claim_submitted",
"external_id": "worker_67890",
"attributes": {
"reference_number": "WIC-2026-0001",
"worker_name": "John Doe",
"incident_date": "2026-03-15T14:30:00+08:00",
"claim_type": "work_injury",
},
"contact": {
"email": "[email protected]",
"first_name": "John",
"timezone": "Asia/Ho_Chi_Minh",
"$add_tags": ["claimant"],
},
"idempotency_key": "claim_submitted_WIC-2026-0001",
},
)
 
assert response.status_code == 202
event = response.json()
print(event["event_id"]) # evt_8f14e45f-...

JavaScript

javascript
const response = await fetch("https://synapse-api.pyrx.tech/v1/events", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-WORKSPACE-ID": "ws_k7x9m2p4",
"X-API-KEY": "psk_live_a1b2c3d4e5f67890abcdef1234567890",
},
body: JSON.stringify({
event_name: "claim_submitted",
external_id: "worker_67890",
attributes: {
reference_number: "WIC-2026-0001",
worker_name: "John Doe",
incident_date: "2026-03-15T14:30:00+08:00",
claim_type: "work_injury",
},
contact: {
email: "[email protected]",
first_name: "John",
timezone: "Asia/Ho_Chi_Minh",
$add_tags: ["claimant"],
},
idempotency_key: "claim_submitted_WIC-2026-0001",
}),
});
 
const event = await response.json();
console.log(event.event_id); // evt_8f14e45f-...

Batch Ingestion

For high-volume use cases or SDK batch uploads, use the batch endpoint to send up to 50 events in a single request.

POST https://synapse-api.pyrx.tech/v1/events/batch

Headers

HeaderRequiredDescription
Content-TypeYesMust be application/json
X-WORKSPACE-IDYesYour workspace identifier
X-API-KEYYesA data-scoped API key

Request Body

json
{
"events": [
{
"event_name": "page_viewed",
"external_id": "user_12345",
"attributes": {"page": "/pricing", "referrer": "google"}
},
{
"event_name": "button_clicked",
"external_id": "user_12345",
"attributes": {"button_id": "cta_signup", "page": "/pricing"}
}
]
}
FieldTypeRequiredDescription
eventsarrayYesArray of event objects (1-50 items). Each event follows the same schema as the single event endpoint.

Response (202 Accepted)

json
{
"accepted": 2,
"rejected": 0
}
FieldTypeDescription
acceptedintegerNumber of events successfully queued
rejectedintegerNumber of events that failed processing

Each event in the batch is processed independently. One failed event does not block others. The source field for batch events is set to sdk.

Note

The batch endpoint checks the monthly events plan limit once before processing. If the limit is reached, the entire batch is rejected with a 403 plan_limit_reached error.


Processing Pipeline

After an event is accepted, Synapse processes it through the following pipeline:

  1. Validate schema -- Verify required fields and attribute types
  2. Upsert contact -- Create or update the contact matching external_id
  3. Store event -- Persist the event to PostgreSQL
  4. Check idempotency -- Skip if this idempotency_key was already processed
  5. Publish to RabbitMQ -- Route to the flow trigger queue for async processing
  6. Flow matching -- Match against all active flows with matching trigger_event
  7. Segment evaluation -- If the flow has a target segment, verify the contact qualifies
  8. Trip creation -- Create a flow trip and execute the flow's steps
Note

Event processing is asynchronous. A 202 Accepted response does not mean the event has been fully processed -- it means it has been durably queued. Use webhooks to track downstream delivery status.