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

# API reference

> Reference for UserJourneys React Native SDK options, event wrapping, link handling, and WebView host props.

Most apps use one setup function and one mounted host. The host owns the
built-in native prompt and the full-screen WebView.

```tsx theme={null}
import {
  createInterviewClient,
  InterviewHost,
} from "@userjourneys/interviews-react-native";
```

## createInterviewClient

Creates a wrapper around your existing analytics client and returns link
handling utilities.

```ts theme={null}
const interviews = createInterviewClient({
  publicKey: "INTERVIEWS_PUBLIC_KEY",
  referenceId: currentUser.id,
});
```

<ParamField body="publicKey" type="string" required>
  The Interviews public SDK key from Settings -> API Keys. It is scoped to
  loading interview triggers and launching eligible in-app interviews.
</ParamField>

<ParamField body="referenceId" type="string | null | undefined">
  Your stable person id. Use the same id you use for your analytics person id,
  such as a Mixpanel `distinct_id`, or your canonical user id.
</ParamField>

<ParamField body="openFallbackUrl" type="(url) => void | Promise<void>">
  Optional override for opening the server-provided browser interview URL.
  Defaults to React Native `Linking.openURL`.
</ParamField>

<ParamField body="mapTrackProperties" type="(eventName, properties) => metadata">
  Optional allowlist mapper for event properties. Only JSON primitive metadata is
  accepted.
</ParamField>

<ParamField body="metadata" type="metadata">
  Optional app-level primitive metadata added to eligible event and link launch requests.
</ParamField>

<ParamField body="onDiagnostic" type="(event) => void">
  Optional low-level diagnostics for logging config loads, ignored events,
  fallbacks, and WebView bridge errors.
</ParamField>

## setUser

Updates the current respondent identity at runtime — call it on sign-in, sign-out,
or account switch. The SDK refetches trigger config for the new identity and
discards any in-flight launch from the previous user, so a pending invite never
opens for the wrong person.

```ts theme={null}
// sign in / switch account
interviews.setUser({ referenceId: currentUser.id });

// sign out — clear the identity
interviews.setUser({ referenceId: null });
```

<ParamField body="referenceId" type="string | null | undefined">
  The stable person id for the new identity (same value you pass to
  `createInterviewClient`). Pass `null` to clear it on sign-out.
</ParamField>

<ParamField body="metadata" type="metadata">
  Optional app-level primitive metadata for the new identity, merged into eligible
  launch requests.
</ParamField>

## wrapTrackClient

Wraps a client that already exposes a `track(eventName, properties, ...extraArgs)`
method, such as the Mixpanel React Native client or an app-owned analytics
helper with the same method shape.

```ts theme={null}
export const analytics = interviews.wrapTrackClient(rawAnalytics);
```

The wrapper returns a proxy with the same surface as the original client.
Non-`track` properties pass through unchanged. The original analytics call still
runs first, and the wrapper preserves the `track` return value. UserJourneys
receives the event name and mapped context in the background and decides whether
to create a pending invite. Interview config, API, or network failures are
reported through `onDiagnostic`; they do not change the wrapped analytics
return value.

If your analytics provider uses a different event method, call that provider and
then call `interviews.track(eventName, properties)` from the same analytics
helper instead of using `wrapTrackClient`.

The SDK does not call UserJourneys for every analytics event. It loads the
active trigger config for the project, caches it for the server-provided TTL,
and ignores unrelated events locally. A matching event creates a pending invite
locally from the cached config — with no network call — so the prompt appears
instantly. The signed launch is resolved with the server only when the user
accepts the prompt.

Matching events do not open the interview immediately. They set
`snapshot.pendingInvite`. By default, `InterviewHost` renders the
prompt for that invite.

## useInterviews

Subscribes to the SDK snapshot so you can render a custom native prompt. When
using a custom prompt, mount `InterviewHost` with
`showPrompt={false}`.

```tsx theme={null}
const { pendingInvite, activeLaunch } = useInterviews(interviews);
```

`pendingInvite` is set after an eligible analytics event or synced audience
match. `activeLaunch` is set when an invite starts or an explicit link opens
in-app.

## startPendingInvite

Starts the current pending invite.

```ts theme={null}
await interviews.startPendingInvite();
```

Returns:

```ts theme={null}
type StartPendingInviteResult =
  | "webview"
  | "external_browser"
  | "not_eligible"
  | "expired"
  | "none";
```

`webview` means the full-screen host can render the in-app interview.
`external_browser` means the SDK opened the fallback URL. `expired` means the
signed launch expired before the user accepted. `not_eligible` means the server
declined the launch after the user accepted a pending audience prompt. `none`
means no pending invite was available or launch resolution failed without
breaking the app.

## dismissPendingInvite

Clears the current pending invite without opening the interview.

```ts theme={null}
interviews.dismissPendingInvite();
```

## handleLink

Handles HTTPS invite links from Intercom, email, push, or any other channel.

```ts theme={null}
await interviews.handleLink("https://app.userjourneys.ai/i/dS8FFCgu");
```

Supported formats:

* `https://app.userjourneys.ai/i/{inviteCode}`
* `https://app.userjourneys.ai/interviews/{inviteCode}`

Returns:

```ts theme={null}
type InterviewLinkResult =
  | { handled: true; mode: "webview" | "external_browser" | "not_eligible" }
  | { handled: false; reason: "not_interview_link" | "resolve_failed" };
```

## InterviewHost

Mounts the built-in prompt and full-screen WebView host.

```tsx theme={null}
<InterviewHost
  client={interviews}
  onStarted={({ sessionId }) => {
    analytics.track("Interview Started", { sessionId });
  }}
/>
```

<ParamField body="client" type="InterviewClient" required>
  The client returned by `createInterviewClient`.
</ParamField>

<ParamField body="visible" type="boolean">
  Optional app-controlled visibility gate. The host still requires an active
  pending invite or active server launch decision.
</ParamField>

<ParamField body="showPrompt" type="boolean">
  Controls whether the built-in pending-invite prompt is rendered. Defaults to
  `true`. Set to `false` when rendering your own prompt with
  `useInterviews`.
</ParamField>

<ParamField body="allowedOrigins" type="readonly string[]">
  Optional extra origins that the WebView may navigate to. The launch and fallback
  origins are allowed automatically.
</ParamField>

<ParamField body="onLoaded" type="(event) => void">
  Called when the web interview reports that the embed loaded.
</ParamField>

<ParamField body="onStarted" type="(event & { sessionId: string }) => void">
  Called when the interview session starts.
</ParamField>

<ParamField body="onCompleted" type="(event & { sessionId: string }) => void">
  Called when the session completes.
</ParamField>

<ParamField body="onAbandoned" type="(event & { sessionId?: string }) => void">
  Called when the participant leaves before completion.
</ParamField>

<ParamField body="onClosed" type="(event & { reason: 'user' | 'system' }) => void">
  Called when the participant closes the in-app interview.
</ParamField>

<ParamField body="onError" type="(error, event) => void">
  Called for WebView load failures, invalid bridge messages, blocked navigation,
  or interview runtime errors reported by the embed.
</ParamField>

## referenceId and triggerEvent

`referenceId` answers who the respondent is. Use your analytics person id or
canonical user id.

`triggerEvent` answers what app event opened the interview. It is attribution
context; Interview targeting and eligibility are server-controlled.

## Attribution fields

SDK snapshots and callbacks can include these server-provided attribution
fields:

<ParamField body="launchRuleId" type="string | undefined">
  The Study Launch Rule that created the pending invite or launch.
</ParamField>

<ParamField body="experimentId" type="string | undefined">
  The study that UserJourneys selected for the launch.
</ParamField>

<ParamField body="audienceId" type="string | undefined">
  The synced audience that matched the current `referenceId`, when the launch
  started from an audience prompt.
</ParamField>

Apps normally do not construct these values. They come from UserJourneys so
in-app WebView launches and browser fallback sessions keep the same attribution.

## Public API boundary

The SDK uses two versioned public endpoints:

* `GET /api/v1/interview-participant/config`
* `POST /api/v1/interview-participant/launches`
* `POST /api/v1/interview-participant/prompt-events`

The app never calls bootstrap, conversation-token, vendor, or raw interview
runtime endpoints. `config` returns server-controlled trigger rules. `launches`
returns a launch decision object: `webview`, `external_browser`, or
`not_eligible`. `prompt-events` records invite prompt funnel phases for
server-side analytics.

## Package boundary

Install `@userjourneys/interviews-react-native` in your app. The SDK includes
the shared interview runtime in its published JavaScript and type
output, so there is no separate UserJourneys core package for app teams to
install or version.
