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

# Configuration

> Every environment variable FirstFlow reads Supabase keys and an optional AI provider. There is no backend to configure.

FirstFlow is configured almost entirely through the Supabase client and provider props you pass in code. The only environment variables it relies on are your Supabase keys and, optionally, an AI provider key for the server SDK. There is no FirstFlow server, so there are no service secrets, ports, or database URLs to manage.

## Supabase

There are three values: a project **URL** and **anon key** (both browser-safe, used by the runtime) and a **service-role key** (server-only, used by `@firstflow/runtime-server`).

The runtime takes plain strings you read them however your stack exposes env. The two browser-safe values must use your bundler's **client-env prefix** so they reach the browser; the service-role key must **never** carry such a prefix.

| Value              | Browser-safe? | Next.js                         | Vite                        | Create React App              | Node (server)               |
| ------------------ | ------------- | ------------------------------- | --------------------------- | ----------------------------- | --------------------------- |
| Project URL        | yes           | `NEXT_PUBLIC_SUPABASE_URL`      | `VITE_SUPABASE_URL`         | `REACT_APP_SUPABASE_URL`      | `SUPABASE_URL`              |
| `anon` key         | yes           | `NEXT_PUBLIC_SUPABASE_ANON_KEY` | `VITE_SUPABASE_ANON_KEY`    | `REACT_APP_SUPABASE_ANON_KEY` |                             |
| `service_role` key | **no**        | `SUPABASE_SERVICE_ROLE_KEY`     | `SUPABASE_SERVICE_ROLE_KEY` | `SUPABASE_SERVICE_ROLE_KEY`   | `SUPABASE_SERVICE_ROLE_KEY` |

```ts theme={null}
// The name is your bundler's; the runtime just wants the string.
const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,        // Next.js
  // import.meta.env.VITE_SUPABASE_URL,         // Vite
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
);
```

<Warning>
  A client-env prefix (`NEXT_PUBLIC_`, `VITE_`, `REACT_APP_`) inlines a value into
  your browser bundle, where end users can read it. Only the URL and `anon` key
  may carry one. The `service_role` key bypasses Row Level Security keep it on
  the server with a plain name, and never commit it.
</Warning>

## AI provider

Needed only if you wrap an LLM client with `@firstflow/runtime-server` or use the [intent classifier](/server/overview). The widget, experiences, persistence, resume, and triggers all run without any AI key.

**FirstFlow reads no API key.** It wraps an LLM client *you* construct, so the key comes from wherever you supply it there is no `FIRSTFLOW`-owned key variable. There are three ways to provide it:

<Tabs>
  <Tab title="Provider default env">
    Each provider SDK reads its own conventional variable. Set it and construct
    the client with no arguments:

    ```bash theme={null}
    ANTHROPIC_API_KEY=sk-ant-...     # the Anthropic SDK reads this
    # or
    OPENAI_API_KEY=sk-...            # the OpenAI SDK reads this
    ```

    ```ts theme={null}
    const ai = ff.wrap(new Anthropic());   // reads ANTHROPIC_API_KEY
    const ai = ff.wrap(new OpenAI());       // reads OPENAI_API_KEY
    ```
  </Tab>

  <Tab title="Your own env name">
    Name the variable anything you like and pass it explicitly:

    ```bash theme={null}
    MY_CLAUDE_KEY=sk-ant-...
    ```

    ```ts theme={null}
    const ai = ff.wrap(new Anthropic({ apiKey: process.env.MY_CLAUDE_KEY }));
    const ai = ff.wrap(new OpenAI({ apiKey: process.env.MY_OPENAI_KEY }));
    ```
  </Tab>

  <Tab title="OpenAI-compatible endpoint">
    For Ollama, vLLM, LM Studio, LocalAI, or a gateway, use `createAIClient` and
    pass the base URL + key explicitly (names are yours):

    ```bash theme={null}
    LLM_BASE_URL=http://localhost:11434/v1
    LLM_API_KEY=not-needed           # many local servers ignore this
    ```

    ```ts theme={null}
    import { createAIClient } from "@firstflow/runtime-server/aiclient";

    const ai = createAIClient({
      baseURL: process.env.LLM_BASE_URL,
      apiKey: process.env.LLM_API_KEY ?? "not-needed",
      persistence,
    });
    ```

    `openai` is an optional peer dependency loaded only by this subpath install
    it when you use `createAIClient`. Anthropic-only deployments never pull it in.
  </Tab>
</Tabs>

In every case `ff.wrap(...)` / `createAIClient(...)` returns a normal client FirstFlow only observes the calls, it never owns the credentials. See the [Server SDK](/server/wrap-llm-client).

## Where the rest of the config lives

Most of what other tools put in environment variables, FirstFlow puts in **code**, because there is no server reading a `.env`:

| Setting                                       | Where it lives                                        |
| --------------------------------------------- | ----------------------------------------------------- |
| Which experiences exist                       | `firstflow/experiences.json` in your repo             |
| End-user identity                             | the `user` prop on `FirstflowProvider`                |
| Persistence backend                           | the `persistence` prop (Supabase adapter or your own) |
| Intent branches, classifier model, hysteresis | arguments to `ff.classifyIntent(...)`                 |
| Capturing prompt/completion content           | `captureContent` on `new Firstflow({...})`            |

## Linking the front-end and back-end

`user.id` ties a person's experiences (front-end) to their LLM activity (server SDK) pass the same one to both. The server SDK also takes a `conversationId`/`sessionId`, an id your own chat code owns, to group a transcript; `FirstflowProvider` does not use it. See [Identity & auth](/self-hosting/auth-and-orgs).

## Next

Lock down table access with [Row Level Security](/self-hosting/oauth-providers), then review [Identity & auth](/self-hosting/auth-and-orgs) and the [production checklist](/self-hosting/production).
