Events
Events are the behavioural fabric of userGist. Every other feature (segments, surveys, push, in-app) reads from them. This page covers declaring them, sending them, and validating them.
What you get
- Schema registry — declare events with named properties and types so the dashboard knows what to autocomplete.
- Validation at ingest — bad types or missing required props get logged to the Dead-letter tab so you can debug instead of silently dropping.
- ClickHouse-backed storage — fast aggregations, sane retention.
- Live feed — see events stream in as you debug.
- Top events report — most-fired events in the last 24h / 7d / 30d.

Declare an event (optional but recommended)
Declarations are optional — userGist accepts undeclared events. But declaring them gives you:
- Type checking at ingest.
- Autocomplete in the segment builder and survey trigger picker.
- A README for new teammates: "what events do we have, and what do they mean?"
- 1
Open Events → New definition
Sidebar → Audience → Events → New definition.
- 2
Name it
snake_case_verbs_in_past_tense—checkout_completed,subscription_upgraded,error_seen. - 3
Add a description
Where it fires, what it means.
- 4
Add properties
For each property: name, type (
string,number,boolean,date,enum), required/optional. - 5
Save
The schema is now enforced on incoming events.
Send events from the SDK
UserGist.track('checkout_completed', {
orderId: 'ord_991',
amountUsd: 49,
currency: 'USD',
})Events are queued in encrypted on-device storage, batched (default 100 events / 15 s), and flushed in the background. They survive app restarts.
Lifecycle events
If you leave the lifecycle toggle on (App settings → General), the SDK auto-fires:
| Event | When |
|---|---|
app_installed | First launch after a fresh install. |
app_opened | Every cold start. |
session_started | New session (30-minute inactivity window). |
session_ended | Session expires. |
These power most starter segments without any instrumentation work.
Sending events server-side
Some events live in your backend (subscription_renewed, dispute_opened). Send them directly:
POST /v1/sdk/ingest
x-write-key: rk_live_...
Content-Type: application/json
{
"userId": "user_42",
"events": [
{
"name": "subscription_renewed",
"timestamp": "2026-02-12T00:00:00Z",
"properties": { "plan": "pro", "monthlyUsd": 19 }
}
]
}See API → Ingest for the full schema.
Dead-letter
Events that fail validation land in the Dead-letter tab with the reason (required property missing, type mismatch on amountUsd: expected number, got string). You can:
- Fix the offending caller and replay.
- Acknowledge (delete) entries you don't care about.
- Edit your schema to accept the offending shape.

API
GET /v1/apps/:appId/event-definitions— listPOST /v1/apps/:appId/event-definitions— createPATCH /v1/apps/:appId/event-definitions/:id— updatePOST /v1/sdk/ingest— send events
Use activity in messages
Event properties can supply dynamic message fields. Choose Latest matching activity for the most recent matching event within a lookback, or Triggering event for the exact event that caused the message. Send display values and navigation values together, such as a show title, string ID and numeric playback position, and reuse that source across related fields. See Personalize messages for the movie example and the difference between historical activity and current user properties.