Skip to content

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

json
{
"event_name": "string (required)",
"external_id": "string (required)",
"attributes": {},
"contact": {},
"occurred_at": "ISO 8601 (optional, defaults to server time)",
"idempotency_key": "string (optional, max 255 chars)"
}
FieldTypeRequiredDescription
event_namestringYesEvent type identifier (1--255 chars, snake_case)
external_idstringYesYour application's unique user identifier. Alias: user_id (deprecated).
attributesobjectNoEvent-specific data (defaults to {})
contactobjectNoContact fields to upsert alongside this event
occurred_atdatetimeNoWhen the event happened (ISO 8601, defaults to server time)
idempotency_keystringNoPrevents duplicate processing (max 255 chars)

Event Name Conventions

Event names identify the type of business action. Use snake_case with a consistent naming pattern.

{domain}_{action}
PatternExamples
Domain + actionorder_completed, claim_submitted, payment_failed
Feature + stateonboarding_started, onboarding_completed
Object + lifecyclesubscription_activated, subscription_cancelled

Rules

  • Use lowercase snake_case only
  • Maximum 255 characters
  • No spaces, hyphens, or special characters
  • Be specific: claim_submitted is better than claim or submitted
Warning

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.

json
{
"attributes": {
"order_id": "ORD-2026-0042",
"amount": 149.99,
"currency": "USD",
"is_first_order": true,
"items": [
{"name": "Widget Pro", "quantity": 2, "price": 49.99},
{"name": "Gadget X", "quantity": 1, "price": 50.01}
],
"shipping_address": {
"city": "Singapore",
"postal_code": "048623"
}
}
}

Supported Types

TypeExampleNLT Access
String"Singapore"{the City from the trigger event}
Number149.99{the Amount from the trigger event, as "currency"}
Booleantrue{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}
NullnullTreated 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.

Tip

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.

json
{
"contact": {
"email": "[email protected]",
"first_name": "Jane",
"last_name": "Doe",
"phone": "+6591234567",
"timezone": "Asia/Singapore",
"locale": "en-SG",
"properties": {
"plan": "growth",
"company": "Acme Corp"
},
"tags": ["enterprise", "active"],
"$add_tags": ["new-tag"],
"$remove_tags": ["old-tag"]
}
}

Available Fields

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.
$add_tagsstring[]Appends tags to the existing list (duplicates ignored).
$remove_tagsstring[]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 tags is provided, replaces the entire list. Use $add_tags/$remove_tags for additive/subtractive changes.
json
// Existing contact properties: { "plan": "free", "company": "Acme" }
// contact.properties: { "plan": "growth", "region": "APAC" }
// Result: { "plan": "growth", "company": "Acme", "region": "APAC" }
Note

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

FieldSourceDescription
occurred_atClient (optional)When the event actually happened. Defaults to server time (UTC) if not provided. ISO 8601 format.
created_atServerWhen 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.

json
{
"event_name": "purchase_completed",
"external_id": "user_456",
"occurred_at": "2026-03-01T10:00:00Z",
"attributes": {
"amount": 50.00
}
}
Note

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

GET https://synapse-api.pyrx.tech/v1/events/attribute-keys?event_name=order_completed

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:

ParameterTypeRequiredDescription
event_namestringYesThe event type to discover attributes for

Response (200 OK):

json
[
{"key": "order_id", "count": 187},
{"key": "amount", "count": 185},
{"key": "currency", "count": 185},
{"key": "items", "count": 142},
{"key": "shipping_address", "count": 98}
]

Each entry includes the attribute key and how many of the sampled events contained that key, sorted by count descending.

Discover Attribute Values

GET https://synapse-api.pyrx.tech/v1/events/attribute-values?event_name=order_completed&attribute_key=currency

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:

ParameterTypeRequiredDescription
event_namestringYesThe event type to discover values for
attribute_keystringYesThe attribute key to get distinct values for

Response (200 OK):

json
[
{"value": "USD", "count": 145},
{"value": "VND", "count": 32},
{"value": "SGD", "count": 8}
]

Results are sorted by count descending and capped at 100 distinct values.

Note

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

ConstraintLimit
Request body1 MB
event_name length255 characters
external_id length255 characters
attributes depth10 levels of nesting
attributes keys200 per event
idempotency_key length255 characters