agent_id — no API key, no server of your own. The platform gates access with three controls: an agent-level public switch (off by default), a required origin allowlist, and rate limiting.
Use this mode for demos, marketing pages, and support bubbles where standing up a backend isn’t worth it. For production apps with their own users, session tokens are usually the better fit — see when to use session tokens instead.
Enable public access
1
Publish the agent
Public sessions always run the agent’s published configuration. An
unpublished agent rejects session creation. See Versions &
publishing.
2
Turn on the public switch
On the agent’s Deploy page in the console, turn on Public access. It
is off by default — until you enable it, sessions require
authentication.
3
Add allowed origins
On the same page, list every web origin that may start sessions under
Allowed origins (
allowed_origins in the API). The allowlist is
required: a request whose Origin doesn’t match any entry is rejected
with 403.Start a session from the browser
PassagentId to the SDK and it creates the session directly with the Fish Audio API, then connects the audio — your page never handles credentials.
Origin matching
An entry matches only on exact scheme + host + port. The host is case-insensitive, and a trailing slash on an entry is tolerated — everything else must match exactly.Rate limiting
Public session creation is rate limited on two dimensions at once: per client IP and per agent. Either limit being exceeded rejects the request. Sessions started on a public agent are billed to the workspace that owns the agent.If legitimate traffic outgrows these limits, or you need per-user quotas, move
to session token authentication — your
backend becomes the gate, and the platform-side public limits no longer apply.
Treat public input as untrusted
On the public path, the entire session request originates in the visitor’s browser. The platform enforces its own guardrails —overrides are filtered against the agent’s allowlist and unauthorized fields are rejected with 400 — but anything that passes through verbatim is attacker-controllable:
metadatais stored and returned exactly as sent, never interpreted by the platform. When you read it back in conversation history or webhooks, treat it as untrusted data — never as proof of who the visitor is.dynamic_variablesare chosen by the page that starts the session. Don’t inject anything through them that the visitor shouldn’t control. See Dynamic variables.
end_user_id, server-set metadata), create sessions from your backend instead.
Errors
Public-mode failures surface asFishAgentError codes on AgentSession.start():
When to use session tokens instead
Public mode trades control for zero setup. Prefer thesessionToken flow — your backend calls the session endpoint with an API key and hands the token to the browser — when any of these apply:
- The agent should stay private rather than callable by anyone who loads your page.
- You need trusted attribution: an
end_user_idandmetadataset by your server, not the browser. - You want server-side control of
overrides,dynamic_variables, ortool_eventsinstead of accepting the browser’s values. - You already authenticate users and want your own quotas per account, not IP-based platform limits.
{ sessionToken } instead of { agentId }. See Authentication for the full flow.
Going further
Authentication
Both auth modes side by side, and the session token flow in full.
Web SDK
Events, methods, and client tools — identical in both modes.
React SDK
Hooks and components for React apps.
Conversation history
Review what public visitors said to your agent.

