Skip to main content
Events are how Traffical sees what happened in your application: which variants users were exposed to and what they did afterwards. Events power metric dashboards, statistical significance calculations, and the optimization engine that learns from user behaviour. There are three kinds of events.

Event types

Exposure events are emitted automatically when the SDK resolves parameters. They record the user’s assignment for each layer they touched.
{
  "type": "exposure",
  "unitKey": "user_789",
  "timestamp": "2026-05-21T10:30:00Z",
  "decisionId": "dec_abc123",
  "layers": [
    {
      "layerId": "layer_checkout",
      "policyId": "policy_color_test",
      "allocationName": "treatment",
      "bucket": 7420
    }
  ]
}
Exposure events are deduplicated within a session, so the same user/assignment combination isn’t tracked over and over.They form the denominator in conversion-rate metrics: how many users were exposed to each variant.

Event definitions

Event definitions describe the track events your system emits. They’re created in three ways:
  1. Config-as-code — declared in .traffical/config.yaml and synced with traffical push
  2. Dashboard — created manually in the UI
  3. Auto-discovered — created automatically the first time the SDK sends an event name the platform hasn’t seen before
events:
  purchase:
    valueType: currency
    unit: USD
    description: Completed purchase

  add_to_cart:
    valueType: count
    description: Item added to cart

  signup:
    valueType: boolean
    description: User signed up

Value types

Value typeDescriptionTypical example
currencyMonetary valuesRevenue in USD
countNumeric countsItems added, pages viewed
rateProportions or percentagesCompletion rate
booleanBinary outcomesConverted / didn’t convert

Property schemas

Events can declare schemas for their payload properties. Schemas serve three purposes: they generate TypeScript types for track(), they let the edge validate incoming events, and they mark which properties are dimensions for slicing metrics in the warehouse-native pipeline.
events:
  purchase:
    valueType: currency
    unit: USD
    schemaEnforcement: warn      # off | warn | reject
    properties:
      order_id:
        type: string
        required: true
        format: uuid
      total:
        type: number
        required: true
        minimum: 0
      payment_method:
        type: string
        required: true
        enum: [credit_card, paypal, apple_pay]
        dimension: true           # usable for breakdowns
schemaEnforcement controls what happens when an event doesn’t match its schema: off skips validation, warn validates and returns warnings in the API response but still accepts the event, reject drops invalid events before they reach the pipeline. Common properties used across many events can be extracted into property groups for reuse:
propertyGroups:
  geo:
    properties:
      market:    { type: string, enum: [US, EU, APAC], dimension: true }
      country:   { type: string, dimension: true }
      currency:  { type: string, pattern: "^[A-Z]{3}$", dimension: true }

events:
  purchase:
    valueType: currency
    propertyGroups: [geo]
    properties:
      order_id: { type: string, required: true }
See Type-safe events for the full end-to-end flow including CLI codegen, SDK type parameters, and dev-mode validation warnings.

Attribution

Events are attributed to decisions via the decisionId field. When the SDK resolves parameters, it generates a decision ID. When you call track() shortly afterwards, the SDK includes the same decisionId so the event is causally linked to the assignment. The SDK supports two attribution modes:
ModeBehaviourUse when
cumulative (default)A track event is attributed to every layer the user has been exposed to in the session.Cross-page funnels (catalog → PDP → checkout) where multiple experiments contributed to the outcome.
decisionA track event is attributed only to the layers from the specific decision it’s tied to.When you need strict single-decision attribution (often warehouse-native analyses).
For batch and offline systems (email sends, push notifications) you can pass decisionId through your external system and back in when the outcome event arrives. See the email / batch pattern. If a track event has no decisionId and no SDK-built attribution array, the pipeline still attributes it: metrics join track events to assignments on unit_key with a temporal constraint (any track event after the user’s first exposure to an allocation counts toward that allocation). This is what makes events from webhooks and batch processes work without any explicit threading. For finer-grained attribution — e.g. strictly tying a click to the specific decision that preceded it, ignoring other decisions in the same session — pass decisionId and set attributionMode: "decision" on the client. See decisions & attribution.

Type-safe events

The CLI can generate TypeScript types from your event definitions:
traffical generate-types
This produces a TrafficalEventProperties map you can use to type your track() calls — invalid event names and property keys become compile errors. See the CLI page for the wrapper pattern.

What happens after an event is sent?

SDKs batch events in memory and flush to Traffical in the background. They’re used for:
  • Metric computation — for dashboards, significance tests, and the optimization engine.
  • Bandit learning — adaptive policies use track events as reward signals; the optimization engine periodically retrains and republishes allocations or coefficients into the bundle.
If you’d rather compute metrics from your own data warehouse instead of (or in addition to) Traffical-native events, see warehouse-native.

Next steps

Decisions & attribution

How decision IDs flow through your application.

Warehouse-native

Compute metrics directly from your data warehouse.

A/B testing

End-to-end experiment with events.

Optimization

Events as reward signals for adaptive policies.