Skip to main content
An agent is the FirstFlow workspace container for one AI product or environment. Everything you build experiences, audience, conversations, theme, and slash commands lives inside an agent, and every SDK call carries the agent’s id so the backend knows which configuration to evaluate. In code the agent is identified by agentId (browser) / firstflowAgentId (server). Both refer to the same record; the two names exist because the browser SDK and the server SDK tag their traffic from opposite sides of the runtime.
Most teams create one agent per product, plus a separate agent for staging vs. production so test traffic never triggers real experiences. An agent is the unit of isolation nothing leaks across agents.

How it works

An agent is not just a folder. It is the routing key for the entire runtime cycle. When a message arrives, the backend looks up the agent, evaluates that agent’s active experiences and audience, composes a widget, and pushes it to that agent’s connected browsers and nothing else. Three identifiers move through the system, all derived from the same agent record. The browser SDK sends agentId; the server SDK tags every observed LLM call with firstflowAgentId; the backend resolves both to an internal workspaceId, which is the room key for realtime delivery. Get the id wrong on either side and traffic is silently routed to a different agent’s experiences. The agent is also the scope of eligibility. Triggers, audience, schedule, frequency, and the optional LLM classifier are all stored on experiences that belong to one agent and evaluated server-side against that agent’s data. The browser cannot reach across agents, and it cannot force a widget to show it sends agentId and reports activity; the backend decides.

What an agent scopes

Every concept in FirstFlow hangs off exactly one agent. These are the things you create and tune per agent:
  • Experiences the guides, tours, surveys, and announcements you ship. Each has a type (guide | tour | survey | announcement), a status (draft | active | paused), a flow graph, and settings.
  • Audience the end-users who interact with your product and the segments you group them into. Audience data is only recorded when the agent’s userTrackingEnabled flag is on.
  • Conversations & sessions every chat session the server SDK observes, keyed by the sessionId you own.
  • Settings widget theme, AI model assignment, the user-tracking policy, and slash commands (covered below).
An experience belongs to one agent and is invisible to every other agent. Moving a product to a new agent means recreating its experiences; there is no cross-agent share.

Keys

Each agent exposes two distinct credentials with opposite trust levels. Using the wrong one in the wrong place is the most common setup mistake. The publishable key (pk_live_…) is browser-safe and ships in client code alongside agentId. It authenticates @firstflow/react and is not a secret it can only do what a visitor’s browser is allowed to do (open a socket, report activity). The API key (ff_live_…) is a server secret read from the FIRSTFLOW_API_KEY environment variable by @firstflow/sdk. It authorizes observed LLM traffic and must never reach the browser.
Never put the ff_live_… API key in client code, a NEXT_PUBLIC_* variable, or a bundled config. It is a full server credential. The browser only ever needs agentId + the pk_live_… publishable key.
In a Next.js app the split looks like this the publishable key is inlined into the client provider, the API key stays in server-only environment:
The agt_8f2c… value is identical on both sides that is what binds the observed conversation on your server to the widgets pushed to the browser. See Wrap your LLM client for the full server setup and the provider reference for the browser side.

Settings worth knowing

Agent settings are configuration that applies to every experience in the agent, set once on the agent’s Settings page rather than per experience. Theme and slash commands are delivered to the browser automatically: when the provider connects, the backend sends an agent.config event and @firstflow/react applies them. You do not fetch or pass them they ride the socket.

Slash commands

Slash commands are agent-level shortcuts a user can trigger from the chat composer. Each AgentCommand either launches an experience or fires a host-handled action key:
The browser reads them from useFirstflow() as commands and dispatches one with triggerCommand(commandId). A type: "experience" command asks the backend to run that experience’s flow for the current user; a type: "action" command emits an action key your app handles itself. See Commands for the rendering and dispatch contract.

Specific cases and limits

The agent boundary is strict, which removes a few options you might expect:
  • Type is per experience, not per agent. An agent freely mixes guides, tours, surveys, and announcements. The immutable guide | tour | survey | announcement choice is made when you create each experience.
  • You cannot trigger a widget from the client. Eligibility lives server-side against the agent’s experiences. The browser SDK calls notifyActivity() and renders what the backend pushes it never decides what shows. This is why client-only integrations cannot force a widget.
  • No cross-agent reuse. Segments, experiences, taxonomy, and commands are all agent-scoped. Staging and production are separate agents with separate keys and separate data on purpose.
  • userTrackingEnabled gates analytics globally. If it is off, per-experience analytics still won’t populate audience or session data for that agent the policy is enforced at ingestion.
When self-hosting, the agent model is unchanged; only the host the SDKs talk to differs. Point the browser at your API with the provider’s apiUrl prop and the server SDK with FIRSTFLOW_API_BASE_URL.

Next

Build something inside the agent with Experiences and Flows & nodes, or wire the SDKs via the browser provider and server wrapper.