> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getflowpilot.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Analytics integration

> FlowPilot tracks flow analytics automatically. Use the analytics callback to also forward those events into your own analytics stack.

FlowPilot already tracks flow analytics for you and sends them to the FlowPilot backend, which powers placement analytics and experiment results. The analytics callback is an extra tap on that same stream: it hands you every event the SDK records so you can forward selected events into Amplitude, Mixpanel, Segment, or your own pipeline.

The callback is **additive**. It does not replace FlowPilot's own ingestion. Both happen for every event.

## Set the callback

```swift theme={null}
public func setAnalyticsCallback(_ callback: @escaping AnalyticsCallback)
// AnalyticsCallback = (AnalyticsEvent) -> Void
```

Set it once at startup, right after `FlowPilot.configure(...)`:

```swift theme={null}
FlowPilot.shared?.setAnalyticsCallback { event in
    print("FlowPilot event:", event.eventName)
}
```

The callback is captured into each flow session when the session is created. Set it **before** you present or create a session, otherwise events from that session will not reach your callback.

## The event

Each call gives you an `AnalyticsEvent`. The useful fields:

| Field                                           | Type                    | Notes                                                                               |
| ----------------------------------------------- | ----------------------- | ----------------------------------------------------------------------------------- |
| `eventName`                                     | `String`                | The event type, for example `flow_complete`. (Encoded on the wire as `event_type`.) |
| `flowId`                                        | `String`                | The flow that produced the event.                                                   |
| `flowVersionId`                                 | `String`                | The exact published version.                                                        |
| `placementId`                                   | `String?`               | The placement that resolved the flow.                                               |
| `experimentId`                                  | `String?`               | Set when the user is in an experiment.                                              |
| `variantId` / `variantName`                     | `String?`               | The assigned variant, when in an experiment.                                        |
| `screenId` / `screenName`                       | `String?`               | The current screen, for screen and interaction events.                              |
| `screenIndex`                                   | `Int?`                  | 0-indexed screen position.                                                          |
| `elementId` / `elementType` / `interactionType` | `String?`               | Set for `element_interaction` events.                                               |
| `revenue` / `currency`                          | `Double?` / `String?`   | Set for `conversion` events.                                                        |
| `properties`                                    | `[String: AnyCodable]?` | Custom properties (for example `product_id` on a conversion).                       |
| `userId` / `sessionId`                          | `String`                | Stable user id and the current session.                                             |
| `timestamp`                                     | `Date`                  | When the event fired.                                                               |

Most fields are optional and only present for the event types that carry them. For the complete field list and per-event meaning, see [Event taxonomy](/reference/event-taxonomy).

## Event names

The SDK fires these automatic event names (from `AutomaticEventType`):

| Event name            | When                                                      |
| --------------------- | --------------------------------------------------------- |
| `flow_start`          | A flow begins.                                            |
| `screen_view`         | A screen appears.                                         |
| `screen_exit`         | A screen is left (carries time on screen).                |
| `flow_complete`       | The flow completes (a `closeFlow` from completion).       |
| `flow_exit`           | The flow is dismissed before completing.                  |
| `experiment_exposure` | The user is assigned to an experiment variant.            |
| `element_interaction` | The user drives an action chain (tap, toggle, and so on). |

Two more event names appear in the stream but are not part of `AutomaticEventType`:

* `conversion` is emitted when you call [`trackConversion`](/ios-sdk/conversions). It carries `revenue`, `currency`, and any `properties` you pass, and it reaches your callback like any other event.
* `resolve_no_flow` is a backend-side event recorded when a placement resolves to no flow. The iOS SDK does not emit it, so do not expect it in this callback.

<Note>
  These names match the backend's event taxonomy exactly, so the names you forward line up with what FlowPilot reports. The Swift `eventName` property maps to the `event_type` field on the wire.
</Note>

## Example: forward to your analytics

Forward completions and conversions, with flow and experiment context attached:

```swift theme={null}
FlowPilot.shared?.setAnalyticsCallback { event in
    switch event.eventName {
    case "flow_complete":
        Analytics.track("Onboarding Completed", properties: [
            "flow_id": event.flowId,
            "variant": event.variantName ?? "control"
        ])

    case "conversion":
        Analytics.track("Purchase", properties: [
            "revenue": event.revenue ?? 0,
            "currency": event.currency ?? "USD",
            "flow_id": event.flowId,
            "experiment_id": event.experimentId ?? ""
        ])

    default:
        break   // ignore the rest
    }
}
```

## Notes

* **It runs in-process.** The callback is invoked synchronously while the SDK records the event. Keep it fast and non-blocking. If you need to do heavy work, hand the event to your own queue and return.
* **It is additive, not a replacement.** FlowPilot still sends every event to its backend. Your callback is an extra copy for your own tools.
* **Most fields are optional.** Guard against `nil`. A field like `revenue` is only set on `conversion` events; `screenId` is only set on screen-scoped events.

## Common mistakes

* **Setting the callback after presenting.** The session captures the callback at creation time. Set it before `presentPlacement` or `createSession`, ideally right after `configure`.
* **Assuming it replaces FlowPilot ingestion.** It does not. FlowPilot tracks independently. If you only wanted to disable FlowPilot's own tracking, that is a separate concern, not this callback.
* **Double-counting purchases.** If you call `trackConversion` and also forward the `conversion` event to a provider that already tracks the purchase elsewhere, you can count it twice. Pick one source of truth per metric.
* **Heavy work in the callback.** Logging, disk writes, or network calls inline will slow the flow. Offload them.

## Related pages

* [Conversions and revenue](/ios-sdk/conversions)
* [Event taxonomy](/reference/event-taxonomy)
* [Dashboard overview](/dashboard/index)
* [Error handling](/ios-sdk/error-handling)
