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

# Embed Widget

> Add the Crow widget to your website

## Installation

Choose your preferred installation method:

<Tabs>
  <Tab title="Script Tag">
    Add the script before `</body>`:

    <CodeGroup>
      ```html HTML theme={null}
      <script
        src="https://api.usecrow.org/static/crow-widget.js"
        data-api-url="https://api.usecrow.org"
        data-product-id="YOUR_PRODUCT_ID"
        data-agent-name="Your Agent Name"
      ></script>
      ```

      ```jsx Next.js theme={null}
      import Script from 'next/script'

      export default function Layout({ children }) {
        return (
          <>
            {children}
            <Script
              src="https://api.usecrow.org/static/crow-widget.js"
              data-api-url="https://api.usecrow.org"
              data-product-id="YOUR_PRODUCT_ID"
              data-agent-name="Your Agent Name"
              strategy="afterInteractive"
            />
          </>
        )
      }
      ```
    </CodeGroup>

    | Attribute            | Required | Description                                                                                     |
    | -------------------- | -------- | ----------------------------------------------------------------------------------------------- |
    | `data-api-url`       | Yes      | `https://api.usecrow.org`                                                                       |
    | `data-product-id`    | Yes      | Your product ID from dashboard                                                                  |
    | `data-agent-name`    | No       | Display name (defaults to "Assistant")                                                          |
    | `data-variant`       | No       | `widget` (default) or `copilot`                                                                 |
    | `data-subdomain`     | No       | Subdomain key for [multi-subdomain endpoints](/multi-subdomain-endpoints)                       |
    | `data-language`      | No       | ISO 639-1 language code (e.g., `es`, `fr`, `ja`). See [Multi-Language](/multi-language)         |
    | `data-context-label` | No       | Label shown in the widget indicating what page the AI sees. See [Context Label](#context-label) |

    After the script loads, use `window.crow()` to pass functions and objects:

    | Command                   | Description                                                                                              |
    | ------------------------- | -------------------------------------------------------------------------------------------------------- |
    | `setIdentityTokenFetcher` | Auto-refreshing JWT function. See [Identity](#user-identity)                                             |
    | `setContext`              | Page context object. See [Context](#page-context)                                                        |
    | `onToolResult`            | Callback on tool completion. See [Callbacks](#tool-result-callbacks)                                     |
    | `registerToolRenderers`   | Custom inline visuals. See [Renderers](#tool-renderers)                                                  |
    | `identify`                | Manual JWT token (alternative to fetcher)                                                                |
    | `registerTools`           | Client-side tool handlers                                                                                |
    | `setContextLabel`         | Visible label for current page context. See [Context Label](#context-label)                              |
    | `open`                    | Programmatically open/expand the widget or copilot                                                       |
    | `close`                   | Programmatically close/collapse the widget or copilot                                                    |
    | `sendMessage`             | Open the widget and send a message programmatically. See [Programmatic Send](#programmatic-send-message) |
  </Tab>

  <Tab title="React SDK">
    Install the SDK:

    ```bash theme={null}
    npm install @usecrow/client @usecrow/ui
    ```

    Add to your app:

    ```tsx theme={null}
    'use client';

    import { CrowWidget } from '@usecrow/ui';
    import '@usecrow/ui/styles.css';

    export function CrowWidgetClient() {
      return (
        <CrowWidget
          productId="YOUR_PRODUCT_ID"
          apiUrl="https://api.usecrow.org"
          agentName="Your Agent Name"
        />
      );
    }
    ```

    | Prop               | Required | Description                                                                                                   |
    | ------------------ | -------- | ------------------------------------------------------------------------------------------------------------- |
    | `productId`        | Yes      | Your product ID from dashboard                                                                                |
    | `apiUrl`           | Yes      | `https://api.usecrow.org`                                                                                     |
    | `agentName`        | No       | Display name (defaults to "Assistant")                                                                        |
    | `getIdentityToken` | No       | Async function returning a JWT. Auto-refreshes on expiry. See [Identity Verification](/identity-verification) |
    | `context`          | No       | Page context object sent with every message. Reactive — updates when the object changes                       |
    | `toolRenderers`    | No       | Map of tool name → React component for inline rendering. See [Tool Renderers](/tool-renderers)                |
    | `onToolResult`     | No       | Callback `(toolName, result) => void` fired when a server-side tool completes                                 |
    | `subdomain`        | No       | Subdomain key for [multi-subdomain endpoints](/multi-subdomain-endpoints)                                     |
    | `language`         | No       | ISO 639-1 language code (e.g., `es`, `fr`, `ja`). See [Multi-Language](/multi-language)                       |
    | `contextLabel`     | No       | Label shown in the widget indicating what page the AI sees. See [Context Label](#context-label)               |
    | `navigate`         | No       | SPA navigation function (e.g. `router.push`) for internal links                                               |

    <Accordion title="Copilot Sidebar">
      The SDK also includes a sidebar component — no provider wrapping needed:

      ```tsx theme={null}
      'use client';

      import { CrowCopilot } from '@usecrow/ui';
      import '@usecrow/ui/styles.css';

      export function App() {
        return (
          <>
            <YourApp />
            <CrowCopilot
              productId="YOUR_PRODUCT_ID"
              apiUrl="https://api.usecrow.org"
              variant="floating"
              position="right"
              width={400}
              getIdentityToken={async () => {
                const res = await fetch('/api/crow-token');
                const { token } = await res.json();
                return token;
              }}
              context={{ pageId: 'current-page-id' }}
              navigate={(path) => router.push(path)}
            />
          </>
        );
      }
      ```
    </Accordion>
  </Tab>
</Tabs>

## Find Your Product ID

1. Go to [app.usecrow.ai/deploy](https://app.usecrow.ai/deploy)
2. Copy your Product ID

***

## User Identity

Authenticate users for persistent conversations and user-specific actions.

<Tabs>
  <Tab title="Script Tag">
    **Option A — Auto-refresh (recommended).** Pass a function that fetches a JWT. The SDK calls it on mount and re-calls it automatically when the token expires (401):

    ```javascript theme={null}
    window.crow('setIdentityTokenFetcher', async () => {
      const res = await fetch('/api/crow-token');
      const { token } = await res.json();
      return token;
    });
    ```

    **Option B — Manual.** Pass a static token (you handle refresh yourself):

    ```javascript theme={null}
    window.crow('identify', {
      token: 'jwt-from-your-backend',  // Required
      name: 'John Doe'                  // Optional
    })

    // On logout
    window.crow('resetUser')
    ```
  </Tab>

  <Tab title="React SDK">
    Pass `getIdentityToken` — the SDK handles calling it on mount and auto-refreshing on expiry:

    ```tsx theme={null}
    import { CrowWidget } from '@usecrow/ui';

    function App() {
      return (
        <CrowWidget
          productId="YOUR_PRODUCT_ID"
          apiUrl="https://api.usecrow.org"
          getIdentityToken={async () => {
            const res = await fetch('/api/crow-token');
            const { token } = await res.json();
            return token;
          }}
        />
      );
    }
    ```
  </Tab>
</Tabs>

See [Identity Verification](/identity-verification) for backend JWT setup.

***

## Client-Side Tools

Register tools the agent can call. The return value from your handler is sent back to the agent, so it can confirm success or handle errors intelligently.

See [Client-Side Tools](/client-side-tools) for full setup.

<Tabs>
  <Tab title="Script Tag">
    ```javascript theme={null}
    window.crow('registerTools', {
      navigateToPage: async ({ path }) => {
        router.push(path)
        return { success: true }  // Agent sees this result
      },
      
      showUpgradeModal: async () => {
        document.getElementById('upgrade-modal').showModal()
        return { shown: true }
      }
    })
    ```
  </Tab>

  <Tab title="React SDK">
    ```tsx theme={null}
    import { CrowWidget } from '@usecrow/ui';

    function App() {
      const tools = {
        navigateToPage: async ({ path }) => {
          router.push(path);
          return { success: true };  // Agent sees this result
        },
        
        showUpgradeModal: async () => {
          document.getElementById('upgrade-modal').showModal();
          return { shown: true };
        }
      };

      return (
        <CrowWidget
          productId="YOUR_PRODUCT_ID"
          apiUrl="https://api.usecrow.org"
          tools={tools}
        />
      );
    }
    ```
  </Tab>
</Tabs>

<Tip>
  Return meaningful results from your tools. The agent uses these to provide accurate feedback like "Done! I've navigated you to Settings" or handle errors gracefully.
</Tip>

***

## Page Context

Give the AI context about the current page so it can perform page-aware actions.

<Tabs>
  <Tab title="Script Tag">
    ```javascript theme={null}
    window.crow('setContext', { itemId: 'abc-123', page: 'settings' });

    // Update on navigation:
    window.addEventListener('popstate', () => {
      window.crow('setContext', { page: location.pathname });
    });
    ```
  </Tab>

  <Tab title="React SDK">
    Pass a `context` object — it's reactive and updates automatically:

    ```tsx theme={null}
    const context = useMemo(() => {
      const match = pathname.match(/\/items\/([^/]+)/);
      return match ? { itemId: match[1] } : {};
    }, [pathname]);

    <CrowWidget context={context} ... />
    ```
  </Tab>
</Tabs>

***

## Context Label

Show users what page context the AI is aware of — a small label displayed at the top of the widget. Unlike `context` (which is invisible data sent to the AI), `contextLabel` is a visible indicator for the user.

<Tabs>
  <Tab title="Script Tag">
    ```javascript theme={null}
    window.crow('setContextLabel', 'Viewing: Blog Post #123');

    // Update on navigation:
    window.addEventListener('popstate', () => {
      window.crow('setContextLabel', getPageLabel());
    });

    // Clear the label:
    window.crow('setContextLabel', '');
    ```
  </Tab>

  <Tab title="React SDK">
    Pass a `contextLabel` string — it's reactive and updates automatically:

    ```tsx theme={null}
    <CrowCopilot
      contextLabel={`Viewing: ${currentPage.title}`}
      ...
    />
    ```

    Or with `CrowWidget`:

    ```tsx theme={null}
    <CrowWidget
      contextLabel="Viewing: Blog Post #123"
      ...
    />
    ```
  </Tab>
</Tabs>

***

## Tool Result Callbacks

React to server-side tool completions — refresh data, show a diff UI, dispatch events, etc.

<Tabs>
  <Tab title="Script Tag">
    ```javascript theme={null}
    window.crow('onToolResult', (toolName, result) => {
      if (toolName === 'propose_revision') {
        showDiffUI(result);
      }
    });
    ```
  </Tab>

  <Tab title="React SDK">
    ```tsx theme={null}
    <CrowWidget
      onToolResult={(toolName, result) => {
        if (toolName === 'propose_revision') showDiffUI(result);
      }}
      ...
    />
    ```
  </Tab>
</Tabs>

***

## Tool Renderers

Render custom visuals inline in the chat when a tool completes — data cards, tables, charts, and more.

Your renderer receives `{ result, status }` and returns one of:

| Return type     | Use case                                 |
| --------------- | ---------------------------------------- |
| **HTML string** | Data cards, tables, simple visuals       |
| **HTMLElement** | Charts via Chart.js, D3, ECharts, Plotly |
| **ReactNode**   | Full React components (React SDK only)   |

<Tabs>
  <Tab title="Script Tag">
    ### Data Card

    Show key metrics with change indicators:

    ```javascript theme={null}
    window.crow('registerToolRenderers', {
      query_analytics_chart: ({ result }) => {
        const r = result || {};
        const color = r.changePercent > 0 ? '#059669' : '#ef4444';
        const arrow = r.changePercent > 0 ? '↑' : '↓';
        return `<div style="padding:12px; border:1px solid #e2e8f0; border-radius:8px; background:#fff; font-family:system-ui,sans-serif;">
          <div style="font-size:13px; font-weight:600; color:#0f172a;">${r.title || 'Metric'}</div>
          <div style="font-size:24px; font-weight:700; color:#2563eb; margin-top:4px;">${r.currentValue || 'N/A'}</div>
          ${r.changePercent != null
            ? `<div style="font-size:12px; color:${color};">${arrow} ${Math.abs(r.changePercent).toFixed(1)}%</div>`
            : ''}
          ${r.dashboard_url
            ? `<a href="${r.dashboard_url}" target="_blank" style="font-size:11px; color:#2563eb; text-decoration:none; margin-top:8px; display:inline-block;">View full chart ↗</a>`
            : ''}
        </div>`;
      }
    });
    ```

    ### Table

    Display structured data in rows and columns:

    ```javascript theme={null}
    window.crow('registerToolRenderers', {
      query_data_table: ({ result }) => {
        const r = result || {};
        const headers = r.columns || [];
        const rows = r.rows || [];
        return `<div style="border:1px solid #e2e8f0; border-radius:8px; overflow:hidden; font-family:system-ui,sans-serif;">
          <table style="width:100%; border-collapse:collapse; font-size:12px;">
            <thead>
              <tr style="background:#f8fafc;">
                ${headers.map(h =>
                  '<th style="padding:8px 12px; text-align:left; font-weight:600; border-bottom:1px solid #e2e8f0;">' + h + '</th>'
                ).join('')}
              </tr>
            </thead>
            <tbody>
              ${rows.map(row =>
                '<tr style="border-bottom:1px solid #f1f5f9;">' +
                  row.map(cell =>
                    '<td style="padding:6px 12px; color:#334155;">' + cell + '</td>'
                  ).join('') +
                '</tr>'
              ).join('')}
            </tbody>
          </table>
        </div>`;
      }
    });
    ```

    ### Chart (Chart.js, D3, ECharts)

    For interactive charts, return an **HTMLElement** instead of a string. The widget mounts it directly:

    ```html theme={null}
    <!-- Load Chart.js (or any charting library) -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

    <script>
    window.crow('registerToolRenderers', {
      query_analytics_chart: ({ result }) => {
        const canvas = document.createElement('canvas');
        canvas.style.width = '100%';
        canvas.style.maxHeight = '200px';
        new Chart(canvas, {
          type: 'line',
          data: {
            labels: result.data.map(d => d.date),
            datasets: [{
              label: result.title,
              data: result.data.map(d => d.value),
              borderColor: '#2563eb',
              tension: 0.3,
            }]
          },
          options: { responsive: true, plugins: { legend: { display: false } } }
        });
        return canvas;  // Return the DOM element
      }
    });
    </script>
    ```

    <Warning>
      **Timing:** `window.crow()` must be called **after** `crow-widget.js` has loaded. In plain HTML this happens naturally (scripts execute in order). In Next.js, use `onLoad`:

      ```jsx theme={null}
      <Script
        src="https://api.usecrow.org/static/crow-widget.js"
        data-product-id="YOUR_PRODUCT_ID"
        data-api-url="https://api.usecrow.org"
        strategy="afterInteractive"
        onLoad={() => {
          window.crow('registerToolRenderers', { ... });
        }}
      />
      ```
    </Warning>

    <Tip>
      The widget uses Shadow DOM for CSS isolation. Use inline `style` attributes — Tailwind classes and external CSS won't apply inside the widget.
    </Tip>
  </Tab>

  <Tab title="React SDK">
    React SDK renderers return **JSX** — full React components with hooks, state, and libraries like Recharts:

    ```tsx theme={null}
    <CrowCopilot
      toolRenderers={{
        query_analytics_chart: ({ result }) => <MyChart data={result.data} />,
      }}
      ...
    />
    ```
  </Tab>
</Tabs>

***

## Programmatic Open / Close

Open or close the widget from your own code — useful for onboarding flows, feature tours, or reacting to in-app events.

```javascript theme={null}
window.crow('open');   // expand the widget / sidebar
window.crow('close');  // collapse it
```

Works with both the floating widget and the copilot sidebar.

<Tip>
  `window.crow('open')` works from anywhere — event handlers, `useEffect`, timeouts, or third-party callbacks. No imports needed.
</Tip>

***

## Programmatic Send Message

Open the widget and send a message in one call — perfect for "search bar → chat" handoffs, CTA buttons, or triggering the agent from your own UI.

```javascript theme={null}
window.crow('sendMessage', { text: 'Find me React developer jobs' });
```

The widget opens automatically (if collapsed) and the message is sent as if the user typed it.

### Example: Hero search bar

Embed your own input on a landing page and hand off to the Crow agent:

```html theme={null}
<input type="text" id="search" placeholder="Ask anything..." />
<button onclick="sendToCrow()">Search</button>

<script>
  function sendToCrow() {
    const text = document.getElementById('search').value;
    window.crow('sendMessage', { text });
  }
</script>
```

You can also pass a plain string:

```javascript theme={null}
window.crow('sendMessage', 'What can you help me with?');
```

<Tip>
  Combine with [Page Context](#page-context) to give the agent full awareness of the current page when the message is sent.
</Tip>

***

## Quick Q\&A

Some users find it more intuitive to read setup instructions in Q\&A format. Here's everything above as quick answers:

<AccordionGroup>
  <Accordion title="How do I add the chat to my app?">
    Install the SDK and drop in one component:

    ```bash theme={null}
    npm install @usecrow/client @usecrow/ui
    ```

    ```tsx theme={null}
    <CrowCopilot
      productId="YOUR_PRODUCT_ID"
      apiUrl="https://api.usecrow.org"
    />
    ```

    That's it. Chat works anonymously out of the box. No provider wrapping needed.
  </Accordion>

  <Accordion title="How do I authenticate users?">
    Two steps:

    1. **Create a backend endpoint** (\~15 lines) that reads your existing session and mints a JWT signed with your Crow verification secret. Crow never sees your auth system — the JWT is the bridge.

    2. **Pass it as a prop:**

    ```tsx theme={null}
    <CrowCopilot
      productId="YOUR_PRODUCT_ID"
      apiUrl="https://api.usecrow.org"
      getIdentityToken={async () => {
        const res = await fetch('/api/crow-token');
        const { token } = await res.json();
        return token;
      }}
    />
    ```

    The SDK calls this on mount and auto-refreshes when the token expires. No `useEffect`, no `setInterval`, no manual calls.

    This enables: conversation history, personalized responses, and user-specific tool actions.

    See [Identity Verification](/identity-verification) for the backend JWT setup.
  </Accordion>

  <Accordion title="How do I give the AI context about the current page?">
    Pass a `context` object. It's reactive — when it changes (e.g., user navigates), the AI automatically knows.

    ```tsx theme={null}
    const context = useMemo(() => {
      const match = pathname.match(/\/items\/([^/]+)/);
      return match ? { itemId: match[1] } : {};
    }, [pathname]);

    <CrowCopilot
      ...
      context={context}
    />
    ```

    This gets injected into the AI's system prompt so it can perform page-aware actions like "edit this item" without asking which one.
  </Accordion>

  <Accordion title="How do I handle SPA navigation for links?">
    Pass your router's push function:

    ```tsx theme={null}
    <CrowCopilot
      ...
      navigate={(path) => router.push(path)}
    />
    ```

    Internal links the AI returns will use your SPA router instead of a full page reload.
  </Accordion>

  <Accordion title="How do I react when the AI calls a tool?">
    **React SDK** — use `onToolResult` and `toolRenderers` as props:

    ```tsx theme={null}
    <CrowCopilot
      onToolResult={(toolName, result) => {
        if (toolName === 'propose_revision') showDiffUI(result);
      }}
      toolRenderers={{
        query_chart: ({ result }) => <MyChart data={result.data} />,
      }}
    />
    ```

    **Script Tag** — use `window.crow()`:

    ```javascript theme={null}
    window.crow('onToolResult', (toolName, result) => {
      if (toolName === 'propose_revision') showDiffUI(result);
    });

    window.crow('registerToolRenderers', {
      query_chart: ({ result }) => `<div>${result.title}: ${result.value}</div>`,
    });
    ```
  </Accordion>

  <Accordion title="What does the full setup look like?">
    Everything is a prop on one component:

    ```tsx theme={null}
    <CrowCopilot
      productId="..."
      apiUrl="https://api.usecrow.org"
      getIdentityToken={fetchToken}
      context={{ itemId: "abc-123" }}
      navigate={(path) => router.push(path)}
      onToolResult={(name, result) => ...}
      toolRenderers={{ ... }}
      variant="floating"
      position="right"
    />
    ```

    No providers, no initializers, no polling.
  </Accordion>
</AccordionGroup>

***

## Troubleshooting

| Issue                | Solution                                              |
| -------------------- | ----------------------------------------------------- |
| Widget not appearing | Check Product ID, verify script/component is rendered |
| Console errors       | Check `apiUrl` is correct                             |
| CORS errors          | Contact support to whitelist your domain              |
