Event Schema
Events in Synapse are flexible JSON payloads with a few required fields and an open attributes object for your domain-specific data.
Event Structure
| Field | Type | Required | Description |
|---|---|---|---|
event_name | string | Yes | Event type identifier (1--255 chars, snake_case) |
external_id | string | Yes | Your application's unique user identifier. Alias: user_id (deprecated). |
attributes | object | No | Event-specific data (defaults to {}) |
contact | object | No | Contact fields to upsert alongside this event |
occurred_at | datetime | No | When the event happened (ISO 8601, defaults to server time) |
idempotency_key | string | No | Prevents duplicate processing (max 255 chars) |
Event Name Conventions
Event names identify the type of business action. Use snake_case with a consistent naming pattern.
Recommended Format
| Pattern | Examples |
|---|---|
| Domain + action | order_completed, claim_submitted, payment_failed |
| Feature + state | onboarding_started, onboarding_completed |
| Object + lifecycle | subscription_activated, subscription_cancelled |
Rules
- Use lowercase
snake_caseonly - Maximum 255 characters
- No spaces, hyphens, or special characters
- Be specific:
claim_submittedis better thanclaimorsubmitted
Event names are used to match flow triggers. Changing an event name after flows reference it will break those flows. Treat event names as a stable contract between your backend and Synapse.
Attributes
The attributes object accepts any valid JSON. These values are accessible in NLT email templates and flow conditions.
Supported Types
| Type | Example | NLT Access |
|---|---|---|
| String | "Singapore" | {the City from the trigger event} |
| Number | 149.99 | {the Amount from the trigger event, as "currency"} |
| Boolean | true | {if the Is First Order from the trigger event is true} |
| Array | [{...}, {...}] | {for item in the Items from the trigger event} |
| Object | {"city": "..."} | {the Shipping Address.City from the trigger event} |
| Null | null | Treated as missing -- fallback or required suppression applies |
Attribute Key Conventions
NLT resolves attribute keys using a flexible matching algorithm. These all refer to the same attribute:
- JSON key:
"reference_number" - NLT access:
{the Reference Number from the trigger event}
The engine converts between snake_case, Title Case, and camelCase automatically. However, for consistency, use snake_case keys in your JSON payloads.
Keep attribute names descriptive. {the Reference Number from the trigger event} is self-documenting in a template. {the ref from the trigger event} is not.
Contact Fields
The contact object upserts fields on the contact record matching external_id. If no contact exists, one is created.
Available Fields
| 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. |
$add_tags | string[] | Appends tags to the existing list (duplicates ignored). |
$remove_tags | string[] | Removes specified tags from the existing list. |
Merge Behavior
- Top-level fields (
email,first_name,last_name,phone,timezone,locale): Overwritten if provided - Properties: Shallow-merged with existing properties. Existing keys not included in the update are preserved.
- Tags: If
tagsis provided, replaces the entire list. Use$add_tags/$remove_tagsfor additive/subtractive changes.
The contact field is optional. If your application maintains contacts separately (e.g., via POST /v1/contacts), you can omit this field and just send events with external_id.
Timestamp Handling
| Field | Source | Description |
|---|---|---|
occurred_at | Client (optional) | When the event actually happened. Defaults to server time (UTC) if not provided. ISO 8601 format. |
created_at | Server | When the event record was persisted in Synapse |
The occurred_at field is a top-level, client-optional timestamp. If omitted, Synapse sets it to the current server time (UTC). If provided, Synapse uses your value as-is. This is useful for backdating events during historical imports.
occurred_at is used for flow scheduling (wait steps) and analytics ordering. If you are importing historical events, set occurred_at to the original event time so that analytics and flow timing are accurate.
Attribute Discovery
Synapse provides two endpoints for discovering event attribute keys and values at runtime. These are used by the flow builder and template editor to offer autocomplete for event attributes.
Discover Attribute Keys
Returns all distinct top-level attribute keys found across recent events of the specified type. Synapse samples up to 200 recent events and collects their keys.
Authentication: JWT (dashboard) -- requires flows:read or templates:read permission.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
event_name | string | Yes | The event type to discover attributes for |
Response (200 OK):
Each entry includes the attribute key and how many of the sampled events contained that key, sorted by count descending.
Discover Attribute Values
Returns distinct values for a specific attribute key. Synapse samples up to 500 recent events and collects the distinct values for the given key.
Authentication: JWT (dashboard) -- requires flows:read or templates:read permission.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
event_name | string | Yes | The event type to discover values for |
attribute_key | string | Yes | The attribute key to get distinct values for |
Response (200 OK):
Results are sorted by count descending and capped at 100 distinct values.
Attribute discovery samples recent events rather than scanning the full history. If an attribute was only used in older events, it may not appear in the results. The sampling window covers the most recent 200 events (for keys) or 500 events (for values) of the specified event type.
Size Limits
| Constraint | Limit |
|---|---|
| Request body | 1 MB |
event_name length | 255 characters |
external_id length | 255 characters |
attributes depth | 10 levels of nesting |
attributes keys | 200 per event |
idempotency_key length | 255 characters |