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

# Dynamic Variables

> Personalize each session with template variables supplied at creation time

An agent's published configuration is shared by every caller — dynamic variables personalize it per session. Write `{{variable_name}}` placeholders in your agent's system prompt or first message and supply values when you create the session. Substitution happens once, when the published configuration is assembled for the session.

```text System prompt theme={null}
You are a support agent for {{company}}. The caller's name is {{customer_name}}
and they are on the {{plan}} plan. Greet them by name.
```

Pass values as a flat object of strings, numbers, or booleans. Variable names must match `[A-Za-z][A-Za-z0-9_]*` (no hyphens or dots), string values are capped at 1,000 characters, and a request can carry at most 50 variables — violations reject session creation with `422`:

<CodeGroup>
  ```bash API (curl) theme={null}
  curl --request POST https://api.fish.audio/v1/agent/sessions \
    --header "Authorization: Bearer $FISH_API_KEY" \
    --header "Content-Type: application/json" \
    --data '{
      "agent_id": "YOUR_AGENT_ID",
      "dynamic_variables": {
        "company": "Acme",
        "customer_name": "Ada",
        "plan": "Pro"
      }
    }'
  ```

  ```javascript JavaScript SDK theme={null}
  import { AgentSession } from "@fishaudio/agent-client";

  // Public agent: the SDK creates the session and sends the variables for you.
  const session = await AgentSession.start({
    agentId: "YOUR_AGENT_ID",
    dynamicVariables: { company: "Acme", customer_name: "Ada", plan: "Pro" },
  });
  ```
</CodeGroup>

<Warning>
  Placeholders with no matching variable are **not** removed — the literal `{{variable_name}}` text stays in the prompt, visible to the model. Make sure every placeholder in your configuration has a value at session creation.
</Warning>

<Note>
  Session records never store dynamic variable values.
</Note>

<Tip>
  You don't need a variable for the current date or time — the agent already knows both, in the session's timezone. See [Time & timezone](/agents/build/time-timezone).
</Tip>

Variables fill placeholders in the configured text. To replace whole configuration fields for a session — the prompt itself, the opener, voice, language — use [overrides](/agents/deploy/authenticated-sessions#overrides) on the same request; `{{placeholders}}` render inside overridden text too.

## Who supplies the values

Where variable values come from depends on the session's [access mode](/agents/deploy/overview#who-may-start-sessions):

| Mode                                                      | Who creates the session            | Who supplies the values                             |
| --------------------------------------------------------- | ---------------------------------- | --------------------------------------------------- |
| `agentId` ([public agents](/agents/deploy/public-agents)) | The SDK, directly from the browser | The browser, via `AgentSession.start()` options     |
| `sessionToken` (private agents)                           | Your backend, with your API key    | Your backend, in its `POST /v1/agent/sessions` call |

In `sessionToken` mode the SDK's `dynamicVariables` option has no effect — the session already exists by the time the token reaches the browser. Attach personalization server-side instead:

```javascript Backend (sessionToken mode) theme={null}
// Your backend endpoint, called by your own frontend
const response = await fetch("https://api.fish.audio/v1/agent/sessions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.FISH_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    agent_id: "YOUR_AGENT_ID",
    dynamic_variables: { customer_name: user.name, plan: user.plan },
  }),
});
const sessionToken = await response.json(); // pass to AgentSession.start({ sessionToken })
```

<Tip>
  In `agentId` mode, values arrive from the end user's browser — treat them as untrusted input, and use `sessionToken` mode when personalization must come from data only your backend knows.
</Tip>

## Going further

<CardGroup cols={2}>
  <Card title="Agent configuration" icon="sliders" href="/agents/build/configuration">
    The fields your placeholders act on.
  </Card>

  <Card title="Overrides" icon="server" href="/agents/deploy/authenticated-sessions#overrides">
    Replace whole configuration fields for one session.
  </Card>

  <Card title="Versions & publishing" icon="code-branch" href="/agents/deploy/versions-publishing">
    Sessions assemble the published configuration.
  </Card>

  <Card title="Web SDK" icon="js" href="/agents/deploy/web-sdk">
    Every `AgentSession.start()` option.
  </Card>
</CardGroup>
