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
Headers
| Header | Required | Description |
|---|---|---|
Content-Type | Yes | Must be application/json |
X-WORKSPACE-ID | Yes | Your workspace identifier |
X-API-KEY | Yes | A data-scoped API key |
Request Body
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
event_name | string | Yes | The event type identifier. Use snake_case (e.g., order_completed, claim_submitted). |
external_id | string | Yes | Your application's unique user identifier. Used to match or create a contact. |
attributes | object | No | Arbitrary JSON object containing event-specific data. Defaults to {}. Accessible in NLT templates via {the Amount from the trigger event}. |
contact | object | No | Fields to upsert on the contact record. See Contact Fields. |
occurred_at | datetime | No | When the event occurred (ISO 8601, defaults to server time). Useful for historical imports. |
idempotency_key | string | No | A unique key to prevent duplicate processing (max 255 chars). See Idempotency. |
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.
| Field | Type | Description |
|---|---|---|
email | string | The contact's email address. Required for email delivery. |
first_name | string | First name. Used in NLT templates as {the user's First Name}. |
last_name | string | Last name. |
phone | string | Phone number. |
timezone | string | IANA timezone (e.g., Asia/Singapore). Used for send-time optimization. |
locale | string | Locale code (e.g., en-SG, vi-VN). Used for localized content. |
properties | object | Custom key-value pairs. Shallow-merged with existing properties. |
tags | string[] | Replaces the contact's entire tag list. Use $add_tags/$remove_tags for surgical updates. |
$add_tags | string[] | Appends tags to the existing list (duplicates ignored). |
$remove_tags | string[] | Removes specified tags from the existing list. |
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)
The event has been accepted and queued for asynchronous processing. Flow matching, trip creation, and email delivery happen in the background.
Error Responses
| Status | Code | Description |
|---|---|---|
400 | validation_error | Missing or invalid required fields |
401 | invalid_api_key | API key is missing, revoked, or invalid |
403 | insufficient_scope | API key does not have data scope |
403 | plan_limit_reached | Monthly events quota exceeded. Upgrade your plan or wait for the next billing cycle. |
409 | duplicate_event | An event with this idempotency_key was already processed |
429 | rate_limit_exceeded | Too many requests -- see Rate Limits |
Examples
curl
Python
JavaScript
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.
Headers
| Header | Required | Description |
|---|---|---|
Content-Type | Yes | Must be application/json |
X-WORKSPACE-ID | Yes | Your workspace identifier |
X-API-KEY | Yes | A data-scoped API key |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
events | array | Yes | Array of event objects (1-50 items). Each event follows the same schema as the single event endpoint. |
Response (202 Accepted)
| Field | Type | Description |
|---|---|---|
accepted | integer | Number of events successfully queued |
rejected | integer | Number 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.
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:
- Validate schema -- Verify required fields and attribute types
- Upsert contact -- Create or update the contact matching
external_id - Store event -- Persist the event to PostgreSQL
- Check idempotency -- Skip if this
idempotency_keywas already processed - Publish to RabbitMQ -- Route to the flow trigger queue for async processing
- Flow matching -- Match against all active flows with matching
trigger_event - Segment evaluation -- If the flow has a target segment, verify the contact qualifies
- Trip creation -- Create a flow trip and execute the flow's steps
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.