> ## Documentation Index
> Fetch the complete documentation index at: https://hanabiaiinc-fish-772-enterprise-versions.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Widget

> Embed a complete voice and chat UI on any website with two lines of HTML — no build step

The widget is the zero-code way to put your agent on a website. A `<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](/agents/deploy/web-sdk), so everything downstream (history, analysis, webhooks) works unchanged.

## Prerequisites

* An agent with a [published version](/agents/deploy/versions-publishing).
* **Public access** enabled on the agent, with your site's origin on the allowed-origins list — see [Public agents](/agents/deploy/public-agents). `localhost` and `127.0.0.1` count as different origins.
* To keep the agent private instead, skip public access and supply session tokens from your backend with [`sessionTokenProvider`](#private-agents).

## Two-line embed

Add the element and the script anywhere on the page:

```html theme={null}
<fish-agent agent-id="your-agent-id"></fish-agent>
<script
  src="https://unpkg.com/@fishaudio/agent-widget-embed"
  async
  type="text/javascript"
></script>
```

`@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.

```javascript theme={null}
import { registerWidget } from "@fishaudio/agent-widget";
registerWidget(); // defines <fish-agent>
```

### 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](#page-events) as callback props — `clientTools` is just a prop:

```tsx theme={null}
import { FishAgentWidget } from "@fishaudio/agent-widget/react";

<FishAgentWidget
  agentId="your-agent-id"
  clientTools={{
    highlight_product: ({ product_id }) => scrollToProduct(product_id),
  }}
  onConnect={({ sessionId }) => console.log(sessionId)}
/>;
```

Every attribute below has a camelCase 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

| Attribute                                                 | Description                                                                                                          |
| --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `agent-id`                                                | Public agent ID. Required unless the `sessionTokenProvider` property is set — see [Private agents](#private-agents). |
| `agent-name`                                              | Display name in the header.                                                                                          |
| `greeting`                                                | Home-screen headline.                                                                                                |
| `proactive-message`                                       | Enables the attention bubble next to the launcher.                                                                   |
| `proactive-delay`                                         | Seconds before the bubble shows. Default `3`.                                                                        |
| `transcript` / `text-input` / `mic-muting`                | Feature switches, on by default; set `"false"` to disable.                                                           |
| `consent`                                                 | `"true"` shows a first-run terms card (default off). Acceptance is remembered in `localStorage`.                     |
| `consent-text`, `terms-url`, `privacy-url`, `consent-key` | Consent copy, linked policies, and the `localStorage` key (default `fish-agent-consent`).                            |
| `position`                                                | `bottom-right` (default), `bottom-left`, `top-right`, `top-left`.                                                    |
| `language`                                                | Pin the session language — see [Overrides](/agents/deploy/authenticated-sessions#overrides).                         |
| `dynamic-variables`                                       | JSON object of `{{name}}` template values. See [Dynamic variables](/agents/build/dynamic-variables).                 |
| `user-id`                                                 | Your end-user identifier, stored on the session.                                                                     |
| `server-url`                                              | Fish API base override. Default `https://api.fish.audio`.                                                            |
| `text-contents`                                           | JSON overriding any UI string (keys in `DEFAULT_TEXTS` of `@fishaudio/agent-widget`).                                |

## Private agents

Keep the agent non-public and set `sessionTokenProvider` 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.

```html theme={null}
<fish-agent agent-name="Support"></fish-agent>
<script>
  document.querySelector("fish-agent").sessionTokenProvider = async () => {
    const response = await fetch("/api/voice-session", {
      method: "POST",
      headers: { Authorization: `Bearer ${appSession.token}` },
    });
    return response.json();
  };
</script>
```

React apps pass the same function as a prop:

```tsx theme={null}
<FishAgentWidget sessionTokenProvider={getSessionToken} />
```

Your backend holds the API key and creates the session with `POST /v1/agent/sessions`; origin checks, user auth, and rate limiting on that endpoint are yours. See [Authenticated sessions](/agents/deploy/authenticated-sessions) 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:

```css theme={null}
fish-agent {
  --fish-accent: #7c3aed;
  --fish-orb-color-1: #c4b5fd;
  --fish-orb-color-2: #4c1d95;
  --fish-radius: 16px;
  --fish-offset-x: 32px;
  --fish-offset-y: 32px;
  --fish-z-index: 999999;
}
```

| Property                                    | Controls                          |
| ------------------------------------------- | --------------------------------- |
| `--fish-accent`                             | Launcher and primary button color |
| `--fish-orb-color-1` / `--fish-orb-color-2` | The voice orb's gradient colors   |
| `--fish-radius`                             | Corner radius of the panel        |
| `--fish-offset-x` / `--fish-offset-y`       | Distance from the viewport edges  |
| `--fish-z-index`                            | Stacking order on your page       |

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 dispatches `CustomEvent`s (bubbling, composed):

| Event                   | `detail`                                                 | When                                                                                                   |
| ----------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `fish-agent:call`       | `{ options }` — **mutable** `AgentSession.start` options | Right before a session starts. Mutate `detail.options` to inject `clientTools`, `overrides`, anything. |
| `fish-agent:connect`    | `{ sessionId }`                                          | Session established.                                                                                   |
| `fish-agent:disconnect` | `{ reason }`                                             | Session ended.                                                                                         |
| `fish-agent:error`      | `{ code, message }`                                      | Start failure or in-call error.                                                                        |

Inbound: dispatch `fish-agent:expand` on the element or `document` to open the panel programmatically.

Registering [client tools](/agents/build/client-tools) is just the `:call` event (React apps pass the `clientTools` prop instead — same injection, wrapped):

```javascript theme={null}
document
  .querySelector("fish-agent")
  .addEventListener("fish-agent:call", event => {
    event.detail.options.clientTools = {
      highlight_product: ({ product_id }) => scrollToProduct(product_id),
    };
  });
```

## Console settings and precedence

On load, the widget anonymously fetches the agent's console widget settings from `GET /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

<CardGroup cols={2}>
  <Card title="Public agents" icon="globe" href="/agents/deploy/public-agents">
    The public switch, origin allowlist, and rate limits behind the widget.
  </Card>

  <Card title="Authenticated sessions" icon="server" href="/agents/deploy/authenticated-sessions">
    The session-token flow your `sessionTokenProvider` implements.
  </Card>

  <Card title="Web SDK" icon="js" href="/agents/deploy/web-sdk">
    The `AgentSession` API underneath the widget, for building your own UI.
  </Card>

  <Card title="Client tools" icon="code" href="/agents/build/client-tools">
    Let the agent trigger actions on the embedding page.
  </Card>
</CardGroup>
