> ## 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 Expo 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` object to `FlowPilot.configure(...)`. After that, you reach the SDK through the same `FlowPilot` singleton you imported.

<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

Call `FlowPilot.configure(...)` with at least an `apiKey` and an `appId`. Do it at module load or in your root component, before any flow is presented.

```tsx theme={null}
import { FlowPilot } from '@flowpilotjs/react-native-sdk';

FlowPilot.configure({
  apiKey: 'fp_live_xxxxxxxxxxxxxxxx',
  appId: 'your-app-id',
  environment: 'production',
  context: {
    'user.id': 'user_123',
    'user.is_premium': false,
  },
});
```

`FlowPilot.configure(...)` validates the configuration synchronously and **throws** if the API key does not start with `fp_`, or if `appId` is empty. Wrap it in a `try/catch` if you build the key from a value that might be missing.

<Warning>
  Configuration throws on an invalid API key, it does not silently no-op. This differs from the iOS SDK (which leaves `FlowPilot.shared` `nil`). If `configure` throws and you swallow it, every later `resolveSession` / `createSession` throws `SDK_NOT_INITIALIZED` instead.
</Warning>

### 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 an environment variable (for example through `app.config.js` / `expo-constants`) or your build's secrets, not a checked-in string.
</Warning>

## Configuration options

`FlowPilotConfiguration` has two required fields; 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="'development' | 'staging' | 'production' | { baseUrl: string }" default="'production'">
  Which FlowPilot backend to talk to, and the default cache freshness. Pass a named environment or a custom `{ baseUrl }` for a self-hosted or test backend. The named environments also set the default cache TTL: `development` is `0` (no caching by default), `staging` is `60` seconds, `production` is `300` seconds. See [Caching](/expo-sdk/caching).
</ParamField>

<ParamField path="context" type="Record<string, unknown>" default="undefined">
  Initial SDK context for variable resolution and audience targeting, as a flat key/value map (for example `{ 'user.id': '123', 'user.is_premium': true }`). Keys can use dot notation to match how variables reference them. See [Variables and context](/expo-sdk/variables-and-context).
</ParamField>

<ParamField path="cachingEnabled" type="boolean" default="true">
  Whether resolved flows are cached (in memory and on disk). Caching makes repeat presents fast and powers offline fallback. Disabling it also disables prefetching. See [Caching](/expo-sdk/caching).
</ParamField>

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

<ParamField path="resolveTimeout" type="number" default="4">
  Hard wall-clock deadline, in **seconds**, for a live resolve before the fail-safe chain falls back to stale cache, then a bundled default, then an error. Values are floored at `0.5`. Hitting the deadline degrades the current caller to a fallback tier but does not cancel the in-flight resolve (it keeps running to warm the cache for the next caller). See [Caching](/expo-sdk/caching).
</ParamField>

<ParamField path="resolveNoFallbackTimeout" type="number" default="12">
  Extended live-resolve deadline, in **seconds**, used **only** when a present-time resolve has nothing to fall back to (fresh and stale cache both miss **and** no bundled default is registered). In that case the short `resolveTimeout` buys nothing, so the SDK waits this much longer for the real flow rather than showing a blank screen. Floored at `resolveTimeout`; set it at or below `resolveTimeout` to disable. See [The no-fallback second chance](/expo-sdk/caching#the-no-fallback-second-chance).
</ParamField>

<ParamField path="prefetchOnLaunch" type="string[]" default="[]">
  Placement keys to warm automatically, once, in the background right after `configure(...)`. Warming 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`). See [Prefetching](/expo-sdk/prefetching).
</ParamField>

<ParamField path="prefetchMediaStrategy" type="'none' | 'firstScreen' | 'allScreens'" 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](/expo-sdk/prefetching).
</ParamField>

<ParamField path="bundledFlows" type="Record<string, object | (() => object | null)>" default="{}">
  Build-time offline default flows, keyed by placement key. Unlike iOS (which loads a bundle resource by name), React Native has no such lookup: pass the flow JSON directly, typically `require('./flows/onboarding.json')` (Metro inlines it as an object), an already-parsed object, or a lazy loader thunk. See [Offline and bundled flows](/expo-sdk/offline-bundled-flows).
</ParamField>

<ParamField path="bundledFlowAssets" type="Record<string, BundledFlowAssets>" default="{}">
  Offline image, icon, and font assets for bundled flows, keyed by the same placement key as `bundledFlows`, so a bundled default renders with no network at all. Usually produced by the `flowpilot-export` CLI. See [Offline and bundled flows](/expo-sdk/offline-bundled-flows).
</ParamField>

<ParamField path="logLevel" type="'none' | 'error' | 'warn' | 'info' | 'debug' | 'verbose'" default="'info'">
  How much the SDK logs to the console. Raise to `debug` or `verbose` to see resolves, the delivery source, and validation failures.
</ParamField>

<ParamField path="disableErrorReporting" type="boolean" 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 error handlers and never touches your app's own
  Sentry/Crashlytics. Set to `true` to disable. See
  [Error handling](/expo-sdk/error-handling#internal-error-reporting).
</ParamField>

<Note>
  The named environments differ mainly in their default cache TTL. Use a custom `{ baseUrl }` to point the SDK at a different host, such as a self-hosted or test backend.
</Note>

## Example

A complete launch configuration with context for targeting, a bundled offline default, and launch prefetch:

```tsx theme={null}
import { FlowPilot } from '@flowpilotjs/react-native-sdk';

FlowPilot.configure({
  apiKey: process.env.EXPO_PUBLIC_FLOWPILOT_API_KEY ?? '',
  appId: 'app_ios_main',
  environment: 'production',
  context: {
    'user.id': 'user_123',
    'user.is_premium': false,
    'user.plan': 'free',
  },
  resolveTimeout: 4,
  bundledFlows: {
    onboarding: require('./flows/onboarding.json'),
  },
  prefetchOnLaunch: ['onboarding'], // warm onboarding in the background
  prefetchMediaStrategy: 'firstScreen',
  logLevel: 'info',
});
```

## Updating context after login

The Expo SDK reads `context` at configure time and when each session is created. There is **no public `updateContext` method** on `FlowPilot` in this build (unlike the iOS SDK). To change targeting **attributes** after login, call `configure` again with the new `context`, before you present the next flow. To change the **user identity**, prefer [`FlowPilot.identify(userId)`](/expo-sdk/variables-and-context#identity-and-the-per-install-user-id) (and `FlowPilot.reset()` on logout) rather than re-configuring.

```tsx theme={null}
// After the user signs in:
FlowPilot.configure({
  apiKey: process.env.EXPO_PUBLIC_FLOWPILOT_API_KEY ?? '',
  appId: 'app_ios_main',
  context: {
    'user.id': 'user_456',
    'user.is_premium': true,
    'user.plan': 'pro',
  },
});
```

## Common mistakes

* **Swallowing the throw.** `configure` throws on a bad API key or empty App ID. Do not wrap it in an empty `catch`, or you will see `SDK_NOT_INITIALIZED` later with no clue why.
* **Presenting before configuring.** A present or resolve before `configure(...)` throws `SDK_NOT_INITIALIZED`. Configure once at startup, before any flow.
* **Hardcoding the API key in committed source.** Inject it from an environment variable or secrets file.
* **App ID from the wrong app.** The `appId` must own the placement you present, or the resolve returns no flow.
* **Expecting `updateContext`.** It does not exist in this build. Re-`configure` to change context.

## Troubleshooting

* **`Invalid API key. Must start with "fp_".`** thrown from `configure`. The `apiKey` is wrong or unset. Copy it again from **Settings -> API Keys** in the dashboard.
* **`App ID is required.`** thrown from `configure`. `appId` is empty. Pass the App ID that owns your placements.
* **Every present throws `SDK_NOT_INITIALIZED`.** `configure` never ran (or threw and was swallowed). Move it to app startup and let any thrown error surface during development.
* **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](/expo-sdk/quickstart) checklist.

## Related pages

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