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

# Introduction

> FirstFlow is an open-source SDK for onboarding, tours, surveys, and LLM observability in AI agent and chat products running entirely on your own infrastructure.

FirstFlow is the onboarding and activation layer for your **AI agent and chat products**. You author experiences AI-composed guides, tours, surveys, and announcements in a visual builder, and deliver them **inside your chat UI** with a drop-in widget. What shows, to whom, and when is decided server-side, so changing an experience never requires a code deploy.

The integration contract is small: install [`@firstflow/sdk`](/server/overview) on your backend, mount [`@firstflow/react`](/react/overview) in your frontend, wrap your existing LLM client, and add a provider plus a widget. From there FirstFlow watches each conversation turn, decides whether anyone qualifies for an experience, composes the widget, and pushes the finished result down a realtime socket.

<Note>
  FirstFlow is **open source (MIT)** and runs entirely on **your own infrastructure** your app, your Supabase project, and your experiences in git. There is no FirstFlow service in the path. Start with [Self-hosting](/self-hosting/overview), or read the source on [GitHub](https://github.com/firstflowdev/firstflow-oss).
</Note>

## What you can build

Everything you ship is an **experience** a unit of content with a `type` that determines how it renders. There are four:

A **guide** is a single AI-composed widget that responds to the conversation in context. You author a flow graph; at runtime the backend walks it to a Result node and Anthropic (`claude-sonnet-4-6`) renders the answer, walkthrough, or CTA as rich blocks. A **tour** is a multi-step flow that walks users through features, rendered directly in chat. A **survey** collects NPS, scales, multiple choice, and open questions, answered natively in the chat surface. An **announcement** is a feature banner or update card rendered inline or as an overlay. See [Experiences](/concepts/experiences) for the full model, and [Widgets & blocks](/concepts/widgets-and-blocks) for how a `WidgetTree` renders to DOM.

Two more capabilities cut across all experience types. [Slash commands](/react/commands) put a `/` palette in your composer that triggers an experience or a host-app action. [Audience targeting](/features/targeting) segments end-users by traits so each experience reaches only the people you intend.

## How it works

The mental model is one rule: **eligibility is evaluated server-side, and finished widgets are pushed to the browser.** The browser SDK is a passive consumer it renders what arrives and reports activity back. That is why you cannot trigger a widget purely from the client: you wrap your LLM call (or call [`notifyActivity()`](/react/use-firstflow)), and the server decides whether anyone qualifies.

Concretely, your wrapped LLM client tags each completion and the SDK fires a fire-and-forget `POST /v1/conversations/:conversationId/messages` to the backend, which returns `202` and processes asynchronously. The backend's `ConversationRouterService` derives a trigger type from the message role (`after_user_message`, `after_user_and_agent_message`, or `conversation_classifier`), finds experiences whose triggers and [audience](/concepts/triggers-and-audience) match, resolves any classifier or branch-decision gate, walks the [flow graph](/concepts/flows-and-nodes) to a Result node, and asks Anthropic to compose a `WidgetTree`. That tree is emitted over Socket.IO to the room `member:{workspaceId}:{userId}`, where [`@firstflow/react`](/react/overview) renders it. When the user interacts, a `widget.action` travels back and flow navigation (`flow.next` / `flow.back`) is resolved **server-side**, composing and pushing the next step.

```mermaid theme={null}
flowchart TD
  A["Host app LLM chat turn"] -->|wrapped client| B["@firstflow/sdk"]
  B -->|"POST /messages (202, fire-and-forget)"| C["backend · ConversationRouterService"]
  C --> D{"Eligible?<br/>triggers + audience + classifier"}
  D -->|no| Z["nothing shown"]
  D -->|yes| E["Walk flow graph → Result node"]
  E --> F["Compose WidgetTree<br/>Anthropic claude-sonnet-4-6"]
  F -->|"widget.show over Socket.IO"| G["@firstflow/react · renders blocks"]
  G -->|"user interacts → widget.action"| C
  C -->|"flow.next / flow.back (server-side)"| E

  style A fill:#fde68a,stroke:#92400e,color:#1c1917
  style B fill:#bbf7d0,stroke:#166534,color:#1c1917
  style C fill:#c7d2fe,stroke:#3730a3,color:#1c1917
  style D fill:#fbcfe8,stroke:#9d174d,color:#1c1917
  style E fill:#a5f3fc,stroke:#155e75,color:#1c1917
  style F fill:#ddd6fe,stroke:#5b21b6,color:#1c1917
  style G fill:#fed7aa,stroke:#9a3412,color:#1c1917
  style Z fill:#e5e7eb,stroke:#6b7280,color:#1c1917
```

For the full architecture authentication, transport, and the realtime room model see [How it works](/how-it-works).

## How it fits your app

FirstFlow plugs in from two sides, and they are linked by one id you own.

On the **browser**, [`@firstflow/react`](/react/overview) opens the realtime connection, identifies the end-user, and renders whatever FirstFlow decides to show. Mount [`FirstflowProvider`](/react/provider) once above your chat, drop in [`FirstflowWidget`](/react/widget), and pass your already-authenticated user FirstFlow trusts that value, so your own auth provider verifies who the user is.

```tsx theme={null}
import { FirstflowProvider, FirstflowWidget } from "@firstflow/react";

<FirstflowProvider
  agentId="agt_…"
  publishableKey="pk_live_…"
  user={{ id: user.id, email: user.email, traits: { plan: user.plan } }}
  conversationId={conversationId}
>
  <YourChat />
  <FirstflowWidget />
</FirstflowProvider>
```

The `publishableKey` (`pk_live_…`) is browser-safe and not a secret. `conversationId` is optional and lazy: omit it until the first message, then mint a stable id and reuse it across reloads. See [Conversations & sessions](/concepts/conversations-and-sessions).

On the **server**, [`@firstflow/sdk`](/server/overview) wraps your existing LLM client so every turn, trace, and cost flows to FirstFlow with no extra calls. Tag each observed call with all three of `firstflowAgentId`, `sessionId`, and `userId` they are stripped from the request body before it reaches OpenAI or Anthropic.

```ts theme={null}
import { OpenAI } from "@firstflow/sdk/openai";

const client = new OpenAI(); // reads FIRSTFLOW_API_KEY + OPENAI_API_KEY from env

const completion = await client.chat.completions.create({
  firstflowAgentId: "agt_…",
  sessionId,        // same id as the browser's conversationId
  userId: user.id,
  model: "gpt-4o",
  messages,
});
```

<Warning>
  All three tags are required. If you provide only some of `firstflowAgentId`, `sessionId`, and `userId`, the call still runs but passes through **unobserved** FirstFlow logs a one-time warning and never sees that turn. Identity is never inferred.
</Warning>

The single shared id `conversationId` in the browser, `sessionId` on the server is what lines up the widget, the stored transcript, the branch-decision classifier, traces, and any flow run onto one conversation. The server SDK reads `FIRSTFLOW_API_KEY` (a secret, server-only) from the environment; never hard-code it and never expose it to the browser. Anthropic and OpenAI-compatible endpoints (Ollama, vLLM, LM Studio) work the same way through [`@firstflow/sdk/anthropic`](/server/wrap-llm-client) and `@firstflow/sdk/aiclient`; LangChain apps attach the [`FirstflowCallbackHandler`](/server/langchain) instead.

## Choose your entry point

Most teams start with the [Quickstart](/quickstart): wrap one LLM call, mount the provider, and watch a live widget appear. From there, the two SDKs are the natural next stops pick the side you are integrating first.

<CardGroup cols={2}>
  <Card title="React SDK" icon="react" href="/react/overview">
    Mount the provider, render the widget, identify users, and run slash commands in the browser.
  </Card>

  <Card title="Server SDK" icon="server" href="/server/overview">
    Wrap your LLM client to observe conversations, traces, costs, and outcomes.
  </Card>
</CardGroup>

If you want the concepts first, read [Core concepts](/concepts); to run the whole platform on your own infrastructure, start with [Self-hosting](/self-hosting/overview); and to drive FirstFlow from a coding agent like Claude Code or Cursor, see the [MCP overview](/mcp/overview).
