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

# Configuration

> Initialize the FlowPilot SDK once at app launch with your API key and App ID.

Configure FlowPilot **once**, as early as possible at app launch, before you present any placement. You pass a `FlowPilotConfiguration` to `FlowPilot.configure(_:)`. After that, you reach the SDK through the `FlowPilot.shared` singleton.

<Info>
  **Before you start**

  You need:

  * A FlowPilot [workspace](/dashboard/workspaces-and-apps).
  * An [app](/dashboard/workspaces-and-apps) inside that workspace.
  * An [SDK API key](/dashboard/api-keys) for that app.
</Info>

## Configure at launch

Build a `FlowPilotConfiguration` with at least an `apiKey` and an `appId`, then call `FlowPilot.configure(_:)`.

<Tabs>
  <Tab title="SwiftUI">
    ```swift theme={null}
    import SwiftUI
    import FlowPilotSDK

    @main
    struct MyApp: App {
        init() {
            FlowPilot.configure(
                FlowPilotConfiguration(
                    apiKey: "fp_live_xxxxxxxxxxxxxxxx",
                    appId: "your-app-id"
                )
            )
        }

        var body: some Scene {
            WindowGroup {
                ContentView()
            }
        }
    }
    ```
  </Tab>

  <Tab title="UIKit">
    ```swift theme={null}
    import UIKit
    import FlowPilotSDK

    @main
    class AppDelegate: UIResponder, UIApplicationDelegate {
        func application(
            _ application: UIApplication,
            didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
        ) -> Bool {

            FlowPilot.configure(
                FlowPilotConfiguration(
                    apiKey: "fp_live_xxxxxxxxxxxxxxxx",
                    appId: "your-app-id"
                )
            )

            return true
        }
    }
    ```
  </Tab>
</Tabs>

`FlowPilot.configure(_:)` checks that the API key starts with `fp_`. If it does not, configuration is skipped, an error is logged, and `FlowPilot.shared` stays `nil`. Every later present call goes through `FlowPilot.shared?`, so a skipped configuration means nothing shows (rather than a crash).

### Where the credentials come from

* **`apiKey`** is an SDK API key for your app. Create one in the dashboard (it is shown only once). It is not your dashboard login. See [API keys](/dashboard/api-keys).
* **`appId`** is the App ID of the app that owns your placements. See [Workspaces and apps](/dashboard/workspaces-and-apps).

<Warning>
  Do not hardcode a live API key in source you commit. Inject it from your build configuration, an `.xcconfig`, or a secrets file that is not checked in.
</Warning>

## Configuration options

`FlowPilotConfiguration` has two required parameters and the rest are optional with sensible defaults.

<ParamField path="apiKey" type="String" required>
  Your workspace SDK API key. Must start with `fp_`. From [API keys](/dashboard/api-keys).
</ParamField>

<ParamField path="appId" type="String" required>
  The App ID that owns your placements. From [Workspaces and apps](/dashboard/workspaces-and-apps).
</ParamField>

<ParamField path="environment" type="FlowPilotEnvironment" default=".production">
  Which FlowPilot backend to talk to. One of `.development`, `.staging`, `.production`, or `.custom(url:)`. Production points at `https://api.flowpilot.io/v1`. Use `.custom(url:)` for a self-hosted or test backend.
</ParamField>

<ParamField path="context" type="SDKContext? ([String: Any])" default="nil">
  Initial SDK context used for variable resolution and audience targeting (for example `["user.id": "123", "user.is_premium": true]`). You can update it later with `updateContext(_:)`. See [Variables and context](/ios-sdk/variables-and-context).
</ParamField>

<ParamField path="cachingEnabled" type="Bool" default="true">
  Whether resolved flows are cached. Caching makes repeat presents fast and powers offline fallback. See [Caching](/ios-sdk/caching).
</ParamField>

<ParamField path="cacheDirectory" type="String?" default="nil">
  Custom directory for the flow cache. `nil` uses a platform-default location.
</ParamField>

<ParamField path="resolveTimeout" type="TimeInterval" default="4.0">
  Hard wall-clock deadline, in seconds, for resolving a placement (including retries and backoff), so a present can never hang on the network. Values are clamped to a minimum of `0.5`. When the deadline is hit, the SDK falls back to cache, then a bundled default, then your host fallback.
</ParamField>

<ParamField path="bundledFlows" type="[String: String]" default="[:]">
  Build-time offline defaults, keyed by placement key. Each value is the base name of a JSON resource in your app bundle (for example `"OnboardingDefault"` loads `OnboardingDefault.json`). See [Offline and bundled flows](/ios-sdk/offline-bundled-flows).
</ParamField>

<ParamField path="bundledFlowAssets" type="[String: BundledFlowAssets]" default="[:]">
  Offline image and font assets for bundled flows, keyed by placement key, so a bundled default renders with no network at all. See [Offline and bundled flows](/ios-sdk/offline-bundled-flows).
</ParamField>

<ParamField path="mediaPreloadingEnabled" type="Bool" default="true">
  Whether images and media are preloaded in screen order when a flow initializes, so users do not see loading states while navigating. See [Media preloading](/ios-sdk/media-preloading).
</ParamField>

<ParamField path="prefetchOnLaunch" type="[String]" default="[]">
  Placement keys to warm automatically, once, in the background right after `configure(_:)`. Warming runs at utility priority and never blocks startup: it caches each flow's JSON and fonts, plus images per `prefetchMediaStrategy`, so a later present hits the cache. No-op when `cachingEnabled` is `false`. Warmed entries only survive their freshness TTL, so this has no visible effect against a `0`-TTL backend (`.development` / `.custom`). See [Prefetching](/ios-sdk/prefetching).
</ParamField>

<ParamField path="prefetchMediaStrategy" type="PrefetchMediaStrategy" default=".firstScreen">
  How aggressively launch prefetch warms images: `.none` (JSON + fonts only), `.firstScreen` (also first-screen and persistent-zone images, the default), or `.allScreens` (also every screen's images). Also bounds the screen window when an explicit `prefetch(_:warmMedia: true)` call opts into media warming. See [Prefetching](/ios-sdk/prefetching).
</ParamField>

<ParamField path="imageMemoryCacheSize" type="Int" default="50 * 1024 * 1024 (50 MB)">
  Maximum in-memory image cache size, in bytes. See [Caching](/ios-sdk/caching).
</ParamField>

<ParamField path="imageDiskCacheSize" type="Int" default="200 * 1024 * 1024 (200 MB)">
  Maximum on-disk image cache size, in bytes. See [Caching](/ios-sdk/caching).
</ParamField>

<ParamField path="debugMode" type="Bool?" default="nil">
  Optional override for the debug overlay. `nil` leaves it off in release builds.
</ParamField>

<ParamField path="logLevel" type="FlowPilotLogLevel" default=".error">
  How much the SDK logs. One of `.none`, `.error`, `.warn`, `.info`, `.debug`, `.verbose`.
</ParamField>

<ParamField path="disableErrorReporting" type="Bool" default="false">
  Opt out of FlowPilot's scoped internal error reporting. By default the SDK
  forwards its **own** internal failures (flow resolve / schema decode / render
  errors) to FlowPilot so we can fix them. It is not a crash reporter: it
  installs no global crash or signal handlers and never touches your app's own
  Sentry/Crashlytics. Set to `true` to disable. See
  [Error handling](/ios-sdk/error-handling#internal-error-reporting).
</ParamField>

## Example

A complete launch configuration with a few context attributes for targeting and variable resolution:

```swift theme={null}
import FlowPilotSDK

FlowPilot.configure(
    FlowPilotConfiguration(
        apiKey: ProcessInfo.processInfo.environment["FLOWPILOT_API_KEY"] ?? "",
        appId: "app_ios_main",
        environment: .production,
        context: [
            "user.id": "user_123",
            "user.is_premium": false,
            "user.plan": "free"
        ],
        resolveTimeout: 4.0,
        bundledFlows: [
            "onboarding": "OnboardingDefault"
        ],
        prefetchOnLaunch: ["onboarding"],   // warm onboarding in the background
        logLevel: .error
    )
)
```

After login (or any time your context changes), update it instead of reconfiguring:

```swift theme={null}
FlowPilot.shared?.updateContext([
    "user.id": "user_456",
    "user.is_premium": true,
    "user.plan": "pro"
])
```

## Common mistakes

* **Configuring more than once.** Configure a single time at launch. Calling `configure(_:)` again replaces the shared instance and resets in-memory state.
* **Presenting before configuring.** If you present before `configure(_:)` runs, `FlowPilot.shared` is `nil` and nothing happens. Configure first, in your `App` `init()` or `application(_:didFinishLaunchingWithOptions:)`.
* **Hardcoding the API key in committed source.** Inject it from build settings or a secrets file instead.
* **Wrong environment.** A `.staging` or `.development` key will not resolve against `.production`, and vice versa. Match the environment to the key.
* **App ID from the wrong app.** The `appId` must be the app that owns the placement you present, or the resolve returns no flow.

## Troubleshooting

* **Nothing presents and `FlowPilot.shared` is `nil`.** The API key did not start with `fp_`, so `configure(_:)` was skipped. Check the logs for "Invalid API key format" and pass a valid SDK key. The SDK exposes this as the `invalidApiKey` error code.
* **An "sdk\_not\_initialized" path is hit.** You presented before configuring. Move `configure(_:)` earlier in launch. See [Error handling](/ios-sdk/error-handling).
* **A resolve returns no flow.** Usually the wrong `appId`, the wrong environment for the key, or a placement with no published flow. Walk the [quickstart](/ios-sdk/quickstart) troubleshooting checklist.

## Related pages

* [Installation](/ios-sdk/installation)
* [iOS quickstart](/ios-sdk/quickstart)
* [Variables and context](/ios-sdk/variables-and-context)
* [Caching](/ios-sdk/caching)
* [Prefetching and readiness](/ios-sdk/prefetching)
* [Error handling](/ios-sdk/error-handling)
* [API keys](/dashboard/api-keys)
* [Workspaces and apps](/dashboard/workspaces-and-apps)
