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.
Event definitions list and live event feed.
Events page — registered definitions on the left, live feed on the right.

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. 1

    Open Events → New definition

    Sidebar → Audience → Events → New definition.

  2. 2

    Name it

    snake_case_verbs_in_past_tensecheckout_completed, subscription_upgraded, error_seen.

  3. 3

    Add a description

    Where it fires, what it means.

  4. 4

    Add properties

    For each property: name, type (string, number, boolean, date, enum), required/optional.

  5. 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:

EventWhen
app_installedFirst launch after a fresh install.
app_openedEvery cold start.
session_startedNew session (30-minute inactivity window).
session_endedSession 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.
Events page. The dead-letter tab lives alongside the live feed.
Events page. Switch to the dead-letter tab to see rejected events.

API

  • GET /v1/apps/:appId/event-definitions — list
  • POST /v1/apps/:appId/event-definitions — create
  • PATCH /v1/apps/:appId/event-definitions/:id — update
  • POST /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.

What's next