<fish-agent> custom element renders the complete experience — a floating launcher that expands into a voice-first chat card with live transcript, typing during the call, and inline tool activity — and a single script tag registers it. Under the hood it runs the same sessions as the Web SDK, so everything downstream (history, analysis, webhooks) works unchanged.
Prerequisites
- An agent with a published version.
- Public access enabled on the agent, with your site’s origin on the allowed-origins list — see Public agents.
localhostand127.0.0.1count as different origins. - To keep the agent private instead, skip public access and supply session tokens from your backend with
sessionTokenProvider.
Two-line embed
Add the element and the script anywhere on the page:@fishaudio/agent-widget-embed is the widget pre-bundled as one IIFE file that registers <fish-agent> on load.
Install from npm
Bundlers can install the element instead:npm install @fishaudio/agent-widget, then call registerWidget() once. Importing the package has no side effects — registration happens only when you call it.
React
React apps get a real component:<FishAgentWidget> registers and renders the element with camelCase props, object props serialized for you, and the page events as callback props — clientTools is just a prop:
dynamicVariables and textContents take objects, and onCall(options) still runs last for anything else. Importing the entry also types the raw <fish-agent> element in JSX, for pages that use the CDN script and install the package only for its types.
Attributes
Private agents
Keep the agent non-public and setsessionTokenProvider instead of an agent-id. It’s a JS property on the element (functions can’t be attributes), called before every session start: fetch the session token from your backend — with whatever auth headers, payload, or credentials the request needs — and return the JSON; it’s used verbatim.
POST /v1/agent/sessions; origin checks, user auth, and rate limiting on that endpoint are yours. See Authentication for the token flow and a backend example.
Theming
Set CSS custom properties on the element. The widget’s internals live in a shadow root — page CSS can’t leak in, but every--fish-* token is public:
Also available:
--fish-accent-text, --fish-bg, --fish-text, --fish-text-secondary, --fish-border, --fish-bubble-agent-bg/-text, --fish-bubble-user-bg/-text, --fish-danger, --fish-live, --fish-fab-size, --fish-font.
Page events
The element dispatchesCustomEvents (bubbling, composed):
Inbound: dispatch
fish-agent:expand on the element or document to open the panel programmatically.
Registering client tools is just the :call event (React apps pass the clientTools prop instead — same injection, wrapped):
Console settings and precedence
On load, the widget anonymously fetches the agent’s console widget settings fromGET /v1/agent/agents/{agent_id}/widget — the same public-plus-origin gate as session creation, so it only answers for public agents to allowed origins. Settings resolve with a fixed precedence: HTML attribute > console widget config > built-in default. The endpoint being unreachable never breaks the widget — it renders from attributes and defaults.
Going further
Public agents
The public switch, origin allowlist, and rate limits behind the widget.
Authentication
The session-token flow your
sessionTokenProvider implements.Web SDK
The
AgentSession API underneath the widget, for building your own UI.Client tools
Let the agent trigger actions on the embedding page.

