Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions agents/build/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ The Voice panel selects the voice profile your agent speaks with (`voice_profile

### Recording

`conversation.record_audio` (default `true`) — store per-speaker audio for [playback and download](/agents/monitor/conversation-history#what-gets-stored), with a per-session override on the [session request](/agents/deploy/authentication). In the console this lives under your agent's **Settings**. Recording defaults to on — make sure callers are informed and consent where your jurisdiction requires it.
`conversation.record_audio` (default `true`) — store per-speaker audio for [playback and download](/agents/monitor/conversation-history#what-gets-stored), with a per-session override on the [session request](/agents/deploy/authenticated-sessions). In the console this lives under your agent's **Settings**. Recording defaults to on — make sure callers are informed and consent where your jurisdiction requires it.

### Timezone

Expand All @@ -80,7 +80,6 @@ The same draft is readable and writable over REST — useful for provisioning ag

### Read the draft

<CodeGroup>
```bash Request
curl --request GET "https://api.fish.audio/v1/agent/agents/$AGENT_ID/config" \
--header "Authorization: Bearer $FISH_API_KEY"
Expand All @@ -107,7 +106,6 @@ curl --request GET "https://api.fish.audio/v1/agent/agents/$AGENT_ID/config" \
}
}
```
</CodeGroup>

The response also includes the `tools`, `knowledge_base`, `analysis`, and `webhooks` sections, each covered on its own page. `config_hash` identifies this draft revision — it changes whenever an edit changes the draft's content, and a mismatch with the published version is what marks an agent as having unpublished changes.

Expand Down
10 changes: 7 additions & 3 deletions agents/build/dynamic-variables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ The session record stores which overrides took effect.

## Who supplies the values

Where variables and overrides come from depends on how the session is [authenticated](/agents/deploy/authentication):
Where variables and overrides come from depends on the session's [access mode](/agents/deploy/overview#who-may-start-sessions):

| Mode | Who creates the session | Who supplies variables and overrides |
|---|---|---|
Expand Down Expand Up @@ -143,8 +143,12 @@ In `agentId` mode, values arrive from the end user's browser — treat them as u
<Card title="Agent configuration" icon="sliders" href="/agents/build/configuration">
The fields your placeholders and overrides act on.
</Card>
<Card title="Authentication" icon="key" href="/agents/deploy/authentication">
API keys, session tokens, and the two session modes.
<Card
title="Authenticated sessions"
icon="server"
href="/agents/deploy/authenticated-sessions"
>
Set variables from your backend when creating the session.
</Card>
<Card title="Versions & publishing" icon="code-branch" href="/agents/deploy/versions-publishing">
Sessions assemble the published configuration.
Expand Down
2 changes: 0 additions & 2 deletions agents/build/knowledge-base.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ Knowledge sources are exposed at `/v1/agent/knowledge-sources`. Only workspace s
| `DELETE /v1/agent/knowledge-sources/{source_id}` | Delete a source |
| `GET /v1/agent/knowledge-sources/{source_id}/agents` | List agents that attach the source |

<CodeGroup>
```bash Create a source
curl --request POST https://api.fish.audio/v1/agent/knowledge-sources \
--header "Authorization: Bearer $FISH_API_KEY" \
Expand All @@ -103,7 +102,6 @@ curl --request PATCH https://api.fish.audio/v1/agent/knowledge-sources/{source_i
curl https://api.fish.audio/v1/agent/knowledge-sources/{source_id}/agents \
--header "Authorization: Bearer $FISH_API_KEY"
```
</CodeGroup>

Only the `source` file part is required on create — `name` falls back to the uploaded file's name.

Expand Down
8 changes: 4 additions & 4 deletions agents/build/time-timezone.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ The timezone is resolved once, when the session is created, taking the first tha

In the browser, this means zero configuration: the SDK detects the visitor's real timezone and the agent talks about "today" in the user's local terms, not yours.

With [session token](/agents/deploy/authentication) auth, the SDK's automatic hint doesn't apply — your backend creates the session, so set `timezone` (or forward the browser's value as `client_timezone`) in the creation request:
With [authenticated sessions](/agents/deploy/authenticated-sessions), the SDK's automatic hint doesn't apply — your backend creates the session, so set `timezone` (or forward the browser's value as `client_timezone`) in the creation request:

```bash API (curl)
curl --request POST https://api.fish.audio/v1/agent/sessions \
Expand Down Expand Up @@ -82,9 +82,9 @@ const session = await AgentSession.start({

<CardGroup cols={2}>
<Card
title="Authentication"
icon="key"
href="/agents/deploy/authentication"
title="Authenticated sessions"
icon="server"
href="/agents/deploy/authenticated-sessions"
>
All session-creation parameters, including `timezone` and `world_context`.
</Card>
Expand Down
4 changes: 2 additions & 2 deletions agents/build/tools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Over the API, `DELETE` is stricter: it returns `409` while the tool is still att

## Manage tools over the API

Every endpoint requires your API key — see [Authentication](/agents/deploy/authentication).
Every endpoint requires your API key — see the [API introduction](/api-reference/introduction).

| Endpoint | Description |
|---|---|
Expand Down Expand Up @@ -102,7 +102,7 @@ Field-level configuration — custom headers, request body templates, timeouts,
<Card title="Web SDK" icon="js" href="/agents/deploy/web-sdk">
Register client tool handlers in your application.
</Card>
<Card title="Authentication" icon="key" href="/agents/deploy/authentication">
<Card title="API introduction" icon="key" href="/api-reference/introduction">
API keys for the endpoints above.
</Card>
</CardGroup>
2 changes: 1 addition & 1 deletion agents/concepts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ curl https://api.fish.audio/v1/agent/agents \
--header "Authorization: Bearer $FISH_API_KEY"
```

See [Authentication](/agents/deploy/authentication) for key setup.
Create keys under [API keys](https://fish.audio/app/api-keys/) in the console.

## Agents

Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,29 @@
---
title: "Authentication"
description: "Connect clients to your agent with a public agent ID or a backend-minted session token"
icon: "key"
title: "Authenticated Sessions"
description: "Create short-lived session tokens on your backend and run private agents in any client"
icon: "server"
---

There are exactly two ways for a client to start a conversation with your agent. Pick one — there is no third path, and your API key is never one of them in the browser.
An authenticated session starts on your server: your backend calls the session endpoint with your API key and receives a short-lived **session token**, which your client uses to connect. The API key never leaves your server, and every session parameter — user identity, overrides, dynamic variables — is set by code you trust. This is how private agents run in production.

<CardGroup cols={2}>
<Card title="Public agent ID" icon="globe">
The browser calls Fish Audio directly with just an `agentId`. No credential
— requires the agent to be [public](/agents/deploy/public-agents).
</Card>
<Card title="Session token" icon="server">
Your backend creates a session with your API key and hands the short-lived
token to the SDK. The default for private agents.
</Card>
</CardGroup>

| | Public agent ID | Session token |
| ------------------------- | --------------------------------------------------------------------------- | ------------------------------------- |
| Credential in browser | None | Short-lived session token |
| Requires | Agent set to public + allowed origins | Your own backend endpoint |
| Session parameters set by | The SDK (`overrides`, `dynamicVariables`, `language`, `toolEvents` options) | Your backend, in the creation request |
| Best for | Demos, marketing pages, low-friction embeds | Production apps with signed-in users |

## Public agents: connect with an agent ID
The token is client-agnostic. Create it the same way regardless of which surface renders the conversation:

If the agent is public, the SDK creates the session itself — no backend, no credential:

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

const session = await AgentSession.start({
agentId: "YOUR_AGENT_ID",
dynamicVariables: { name: "Ada" },
});
```
| Client | Where the token goes |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| [Web SDK](/agents/deploy/web-sdk) | `AgentSession.start({ sessionToken })` |
| [React SDK](/agents/deploy/react-sdk) | `startSession({ sessionToken })` |
| [Widget](/agents/deploy/widget#private-agents) | Returned from `sessionTokenProvider` |
| [Custom client](/agents/deploy/protocol) | Connect to the transport named in the response, per the [wire protocol](/agents/deploy/protocol) |

Fish Audio accepts the request only when the agent has public access enabled and the page's `Origin` is on the agent's allow-list; requests are rate-limited per IP and per agent. Origins match on exact scheme, host, and port — `localhost` and `127.0.0.1` are different origins, so list both during development. See [Public agents](/agents/deploy/public-agents) for setup.
<Note>
No backend, and anyone may talk to the agent? A [public
agent](/agents/deploy/public-agents) lets the SDK create sessions with just an
`agentId` — no token involved, gated by an origin allowlist and rate limits.
</Note>

## Session tokens: mint on your backend
## Create a token on your backend

For private agents, your backend exchanges your API key for a single-conversation token.
Your backend exchanges your API key for a single-conversation token.

<Steps>
<Step title="Create a session from your backend">
Expand All @@ -51,7 +33,7 @@ For private agents, your backend exchanges your API key for a single-conversatio
<Step title="Return the response to your frontend">
The response is the session token. Forward it verbatim.
</Step>
<Step title="Start the SDK with the token">
<Step title="Start the client with the token">
Pass the object to `AgentSession.start({sessionToken})` unmodified. The SDK
handles the connection from there.
</Step>
Expand Down Expand Up @@ -109,6 +91,10 @@ def create_voice_session(end_user_id: str, name: str) -> dict:
return response.json()
```

</CodeGroup>

On the client, fetch the token from your backend and pass it to the SDK unchanged:

```javascript Browser
import { AgentSession } from "@fishaudio/agent-client";

Expand All @@ -119,16 +105,15 @@ const sessionToken = await fetch("/voice-session", { method: "POST" }).then(r =>
const session = await AgentSession.start({ sessionToken });
```

</CodeGroup>
More on what the SDK can do once connected is in the [Web SDK](/agents/deploy/web-sdk) reference.

<Note>
In session token mode, `overrides`, `dynamic_variables`, `language`,
`tool_events`, `timezone`, and `world_context` belong in your backend's
creation request — the SDK forwards these options only in public agent ID
mode.
`overrides`, `dynamic_variables`, `language`, `tool_events`, `timezone`, and
`world_context` belong in your backend's creation request — the SDK forwards
these options only in [public agent](/agents/deploy/public-agents) mode.
</Note>

### Request fields
## Request fields

| Field | Type | Description |
| ------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
Expand All @@ -147,7 +132,7 @@ const session = await AgentSession.start({ sessionToken });

Unknown fields — top-level or inside `overrides` — are rejected with `422`.

### Response
## Response

```json JSON
{
Expand All @@ -163,20 +148,20 @@ Unknown fields — top-level or inside `overrides` — are rejected with `422`.
| Field | Description |
| ---------------------- | ----------------------------------------------------------------------------------------------------------- |
| `session_id` | The session's id — use it later to look up the [conversation record](/agents/monitor/conversation-history). |
| `expires_at` | Deadline for the client to connect. Mint the token right before starting, not ahead of time. |
| `expires_at` | Deadline for the client to connect. Create the token right before starting, not ahead of time. |
| `max_duration_seconds` | Hard cap on session length. |
| `transport` | Which transport the SDK uses for this session; `livekit` today. |
| `livekit_url`, `token` | Connection details for that transport, consumed by the SDK. |

Treat the response as opaque and pass it to `start()` unmodified. If the SDK does not recognize the `transport` value, it fails fast with an `unsupported_transport` error asking you to upgrade the SDK — it never silently degrades.

### Token lifetime
## Token lifetime

- A session token is **single-use**: `start()` consumes it once to establish the conversation.
- On network drops the SDK reconnects at the transport level using the same connection state — it never re-creates the session, so you never need to re-mint mid-call.
- Once a session ends, the token is spent. Mint a new token for each conversation.
- On network drops the SDK reconnects at the transport level using the same connection state — it never re-creates the session, so you never need a fresh token mid-call.
- Once a session ends, the token is spent. Create a new token for each conversation.

### Ending sessions from your backend
## Ending sessions from your backend

A session normally ends from the client side — the user disconnects, or the agent [hangs up](/agents/build/system-tools). To force-end a live session server-side, call the end endpoint with your API key and the `session_id` from the creation response:

Expand Down Expand Up @@ -216,14 +201,18 @@ The complete status-code reference for every `/v1/agent` endpoint — error shap

<CardGroup cols={2}>
<Card title="Public agents" icon="globe" href="/agents/deploy/public-agents">
Enable credential-free access with origin allow-lists.
The credential-free alternative, with origin allow-lists.
</Card>
<Card
title="Widget with private agents"
icon="puzzle-piece"
href="/agents/deploy/widget#private-agents"
>
Feed the widget session tokens from your backend via `sessionTokenProvider`.
</Card>
<Card title="Web SDK" icon="js" href="/agents/deploy/web-sdk">
Everything `AgentSession` can do once connected.
</Card>
<Card title="React SDK" icon="react" href="/agents/deploy/react-sdk">
Hooks and components for React apps.
</Card>
<Card
title="Dynamic variables"
icon="brackets-curly"
Expand Down
Loading