Automatic events
These events are emitted automatically during a flow:Event (eventName) | When it fires |
|---|---|
flow_start | A session starts. |
screen_view | A new screen is displayed. |
screen_exit | The user navigates away from a screen (carries time_on_screen_ms). |
element_interaction | The user taps, toggles, or changes a component (carries interaction_type). |
flow_complete | The flow ended via a closeFlow action or the final screen. |
flow_exit | The flow was dismissed before completion. |
experiment_exposure | The user was assigned to an A/B test variant. |
conversion | You called FlowPilot.trackConversion(...). |
delivery_source (network, cache, stale_cache, or bundled_default) so offline and fallback renders are distinguishable from live network resolves in the dashboard. See Caching.
These event names match the backend’s accepted event types and the iOS SDK exactly. The canonical list and field definitions live in the Event taxonomy reference.
Forward events to your own analytics
Register a callback to mirror every FlowPilot event into your own analytics provider (Amplitude, Mixpanel, Segment, PostHog, and so on). It fires for every event the SDK emits, before the event goes to the FlowPilot backend.properties is the event’s property bag enriched with the flow’s flow_id and the current screen_id, so you can attribute the event without plumbing context yourself. Set the callback once at startup, after configure(...).
The callback is additive: it forwards events, it does not replace FlowPilot’s own delivery. Events still go to the FlowPilot backend regardless of what your callback does. A callback that throws is caught and logged, so it cannot break tracking.
How events are delivered
Understanding the delivery model helps explain why an event might appear a little later than it fired:- Batched. Events are sent in batches of 10, or every 30 seconds, whichever comes first.
- Flushed on background. When the app goes to the background (or iOS reports
inactive), the queue is flushed, so leaving the app does not lose recent events. - Persisted to disk. The queue is written to disk with
expo-file-systembetween launches. A force-quit mid-flow does not lose data: persisted events replay on the next launch before the first flush. - Retried, then dropped. A failed send is retried up to 3 times; after that the batch is dropped to avoid an unbounded queue. The in-memory queue is capped at 100 events.
- Deduplicated server-side. Each event has a stable
event_id, and the backend deduplicates, so a replayed batch after a crash does not double-count.
Conversions
Revenue is tracked separately, because it originates from your purchase flow, not the UI. See Conversions and revenue fortrackConversion, which emits a conversion event attributed to the most-recently-presented flow.
Common mistakes
- Doing heavy work in the callback. It fires on the event path. Keep it light (hand off to your analytics SDK) so you do not stall tracking.
- Setting the callback after presenting. Register it once at startup so it catches
flow_startand everything after. - Expecting instant delivery in the dashboard. Events are batched (10 or 30s). For a quick check, background the app to force a flush, or set
logLevel: 'debug'. - Assuming a force-quit loses events. It does not; queued events persist to disk and replay next launch. Missing events are usually a network failure after retries, visible in the debug log.
Troubleshooting
- Events not appearing. Set
logLevel: 'debug'to see batch flushes. Common causes: the app force-quit before the 30s timer (events replay next launch, they are not lost), or a network error after retries are exhausted (aLogger.warnline in the log). On-disk persistence needsexpo-file-system; confirm it is installed as a peer dependency. - Duplicate events in the dashboard. Expected and harmless on a replay-after-crash; the backend deduplicates by
event_id. conversionmissing. A conversion before any flow is presented is dropped. See Conversions.