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

# Conversions and revenue

> Report a purchase so it attributes to the flow, screen, and experiment variant the user saw.

When a user buys something after seeing a flow, tell FlowPilot. A conversion event ties the revenue back to the exact flow, flow version, placement, screen, and experiment variant that was on screen, so your experiment results and placement analytics can measure what actually drove the purchase.

## The API

`trackConversion` exists on both the SDK singleton and a `FlowSession`. Both are public and have the same signature.

```swift theme={null}
// On the singleton: routes through the most-recently-started session.
public func trackConversion(
    amount: Double,
    currency: String,
    productId: String? = nil,
    metadata: [String: Any]? = nil
)

// On a session you hold: attributes to that specific flow.
public func trackConversion(
    amount: Double,
    currency: String,
    productId: String? = nil,
    metadata: [String: Any]? = nil
)
```

| Parameter   | Type             | Meaning                                                                                                                      |
| ----------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `amount`    | `Double`         | Purchase amount in `currency`'s decimal units (for example `9.99`). Pass `0` for a non-monetary conversion such as a signup. |
| `currency`  | `String`         | ISO 4217 code (for example `"USD"`). The SDK uppercases it before sending.                                                   |
| `productId` | `String?`        | Optional product identifier (for example an Apple IAP product id). Sent under the `product_id` property.                     |
| `metadata`  | `[String: Any]?` | Optional extra properties. A `product_id` set here takes precedence over the `productId` parameter.                          |

### Which one to call

* Use **`FlowPilot.shared?.trackConversion(...)`** for purchases that complete outside the flow, such as a StoreKit transaction callback that fires seconds after the flow has already dismissed. The SDK keeps a strong reference to the most-recently-started session so the event still attributes correctly.
* Use **`session.trackConversion(...)`** when you are holding the `FlowSession` (a SwiftUI `FlowPresenterView` integration) and want to attribute to that exact session.

The conversion is sent as an event with type `conversion` and carries the session's full context: `flow_id`, `flow_version_id`, `placement_id`, `experiment_id`, `variant_id`, and the current screen. See [Event taxonomy](/reference/event-taxonomy) for the event shape.

## Example: attributing a StoreKit purchase

Call `trackConversion` once the purchase is confirmed, from your transaction handler:

```swift theme={null}
import StoreKit

func handle(_ transaction: Transaction) async {
    // Purchase is confirmed at this point.
    FlowPilot.shared?.trackConversion(
        amount: 9.99,
        currency: "USD",
        productId: transaction.productID   // e.g. "pro_monthly"
    )
    await transaction.finish()
}
```

If you are embedding a flow with SwiftUI and still hold the session, attribute to it directly:

```swift theme={null}
session.trackConversion(
    amount: 9.99,
    currency: "USD",
    productId: "pro_monthly",
    metadata: ["billing_period": "monthly"]
)
```

## Notes

* **It does not close the flow.** Tracking a conversion only records the event. Dismiss or complete the flow separately (for example with a `closeFlow` action on a button).
* **Calling after the flow dismisses is fine.** The session keeps its configured context for its lifetime, so an IAP callback that fires after the user has left the flow still attributes to the right flow and variant.
* **No active session means the event is dropped.** `FlowPilot.shared?.trackConversion(...)` logs a warning and drops the event if no flow has been presented yet, because the backend requires flow context. Only call it after `presentPlacement` or `createSession` has run.

## Common mistakes

* **Tracking before the purchase confirms.** Call `trackConversion` only after the transaction succeeds, not when the user taps Buy. A failed or cancelled purchase should not produce a conversion.
* **Expecting it to dismiss the flow.** It does not. Close the flow with its own action.
* **Calling it before any flow was presented.** The singleton has no session to attribute to and the event is dropped. Present a flow first, or hold the session and call `session.trackConversion`.
* **Inconsistent currency codes.** Use ISO 4217 codes. The SDK uppercases the value, but sending `"usd"` and `"US Dollars"` in different places will fork your reporting.

## Related pages

* [Variables and SDK context](/ios-sdk/variables-and-context)
* [Analytics integration](/ios-sdk/analytics)
* [Reading experiment results](/dashboard/reading-results)
* [Event taxonomy](/reference/event-taxonomy)
