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

# CDN / Script Tag

> Add the ThunderPhone voice widget to any website without a bundler

The CDN build bundles React internally, so you can use the widget on static sites, WordPress, Webflow, or any page where you can add HTML. No npm, no bundler, no framework required.

## CDN URLs

<CodeGroup>
  ```html Latest (recommended) theme={null}
  <link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
  <script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
  ```

  ```html Pinned version theme={null}
  <!-- Each stable release also publishes an immutable /widget/vX.Y.Z/ path
       matching the @thunderphone/widget npm version (v1.1.2 at the time of
       writing). If you pin, you own updating the URL for new releases. -->
  <link rel="stylesheet" href="https://cdn.thunderphone.com/widget/v1.1.2/style.css" />
  <script src="https://cdn.thunderphone.com/widget/v1.1.2/widget.js"></script>
  ```
</CodeGroup>

<Tip>
  The `latest` URLs are what the dashboard's embed snippet generator produces. They are cached for 5 minutes and pick up new stable releases automatically. Pinned `/widget/vX.Y.Z/` paths are immutable and cached long-term -- if you pin, match the version to the `@thunderphone/widget` npm release you develop against.
</Tip>

***

## Basic Usage

```html theme={null}
<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
</head>
<body>

  <div id="thunderphone"></div>

  <script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
  <script>
    ThunderPhone.mount({
      element: '#thunderphone',
      publishableKey: 'pk_live_your_publishable_key',
    })
  </script>

</body>
</html>
```

The widget mounts into the target element and renders as a **fixed-position bar in a corner of the viewport** (default `bottom-right`, controlled by the `position` option). The mount element is only the React root -- its location in the page does not affect where the widget appears.

***

## Mount Options

`ThunderPhone.mount()` accepts the same options as the React component, plus the `element` property:

| Option           | Type                                                           | Required | Default                                  | Description                                                                                                                                     |
| ---------------- | -------------------------------------------------------------- | -------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `element`        | `string \| HTMLElement`                                        | Yes      | --                                       | CSS selector (e.g., `'#thunderphone'`) or a DOM element reference.                                                                              |
| `publishableKey` | `string`                                                       | Yes      | --                                       | Publishable API key (`pk_live_...`). The agent is resolved automatically from the key's widget configuration.                                   |
| `theme`          | `'light' \| 'dark'`                                            | No       | `'light'`                                | Color scheme.                                                                                                                                   |
| `primaryColor`   | `string`                                                       | No       | `'#000000'` (light) / `'#ffffff'` (dark) | CSS color string used as the accent color.                                                                                                      |
| `title`          | `string`                                                       | No       | `'Voice assistant'`                      | Text displayed in the widget bar.                                                                                                               |
| `position`       | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | No       | `'bottom-right'`                         | Fixed viewport position.                                                                                                                        |
| `apiBase`        | `string`                                                       | No       | `'https://api.thunderphone.com/v1'`      | API base URL override.                                                                                                                          |
| `language`       | `string`                                                       | No       | --                                       | Per-session language override -- a language code or locale such as `en`, `es`, or `fr-FR`. When unset, the agent's configured language applies. |
| `voice`          | `string`                                                       | No       | --                                       | Per-session voice override -- a voice name such as `maria`. When unset, the agent's configured voice applies.                                   |
| `context`        | `string`                                                       | No       | --                                       | Per-session factual page or site context passed to the agent. Truncated server-side to 12,000 characters.                                       |
| `onConnect`      | `() => void`                                                   | No       | --                                       | Called when the voice session connects.                                                                                                         |
| `onDisconnect`   | `() => void`                                                   | No       | --                                       | Called when the session ends.                                                                                                                   |
| `onError`        | `(error) => void`                                              | No       | --                                       | Called on errors. Error has `error` (code) and `message` fields.                                                                                |
| `ringtone`       | `boolean \| string`                                            | No       | `false`                                  | Play a ringtone while connecting. `true` for the default ringtone, or a URL string for custom audio.                                            |

***

## Cleanup

`ThunderPhone.mount()` returns a widget instance with cleanup methods:

```html theme={null}
<script>
  var widget = ThunderPhone.mount({
    element: '#thunderphone',
    publishableKey: 'pk_live_your_publishable_key',
  })

  // Later, when you want to remove the widget:
  widget.unmount()

  // Or equivalently:
  widget.destroy()
</script>
```

Both `unmount()` and `destroy()` do the same thing -- they disconnect any active voice session and remove the widget from the DOM. Use whichever reads better in your code.

<Note>
  Always clean up the widget when navigating away in single-page applications or when the containing element is removed. This ensures active voice sessions are properly disconnected.
</Note>

***

## Examples

### Dark Theme with Custom Color

```html theme={null}
<div id="thunderphone"></div>

<link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
<script>
  ThunderPhone.mount({
    element: '#thunderphone',
    publishableKey: 'pk_live_your_publishable_key',
    theme: 'dark',
    primaryColor: '#8b5cf6',
    title: 'Talk to our AI',
  })
</script>
```

### Custom Position

```html theme={null}
<div id="thunderphone"></div>

<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
<script>
  ThunderPhone.mount({
    element: '#thunderphone',
    publishableKey: 'pk_live_your_publishable_key',
    position: 'bottom-left',
  })
</script>
```

### With Event Callbacks

```html theme={null}
<div id="thunderphone"></div>

<link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
<script>
  ThunderPhone.mount({
    element: '#thunderphone',
    publishableKey: 'pk_live_your_publishable_key',
    onConnect: function () {
      console.log('Call started')
    },
    onDisconnect: function () {
      console.log('Call ended')
    },
    onError: function (error) {
      console.error('Widget error:', error.error, error.message)
    },
  })
</script>
```

### With Ringtone

```html theme={null}
<div id="thunderphone"></div>

<link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
<script>
  ThunderPhone.mount({
    element: '#thunderphone',
    publishableKey: 'pk_live_your_publishable_key',
    ringtone: true, // or a custom URL: 'https://example.com/ringtone.mp3'
  })
</script>
```

### Using a DOM Element Reference

```html theme={null}
<div id="thunderphone"></div>

<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>
<script>
  var container = document.getElementById('thunderphone')

  ThunderPhone.mount({
    element: container,
    publishableKey: 'pk_live_your_publishable_key',
  })
</script>
```

### WordPress / CMS Integration

Add this to a Custom HTML block or your theme's footer:

```html theme={null}
<link rel="stylesheet" href="https://cdn.thunderphone.com/widget/latest/style.css" />
<script src="https://cdn.thunderphone.com/widget/latest/widget.js"></script>

<div id="thunderphone-widget"></div>
<script>
  ThunderPhone.mount({
    element: '#thunderphone-widget',
    publishableKey: 'pk_live_your_publishable_key',
  })
</script>
```

<Tip>
  The `<div>` can go anywhere -- it is only the mount point. The widget itself renders as a fixed overlay in a viewport corner (set by the `position` option) and does not flow with surrounding content.
</Tip>

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="Widget doesn't appear">
    Make sure both the CSS and JS files are loaded. Check the browser console for network errors. Verify that the target element exists in the DOM before calling `ThunderPhone.mount()`.
  </Accordion>

  <Accordion title="ThunderPhone is not defined">
    The script has not loaded yet. Ensure the `<script>` tag for `widget.js` appears before your mount call, or wrap the mount call in a `DOMContentLoaded` listener.
  </Accordion>

  <Accordion title="Domain not allowed error">
    Add your domain to the allowed domains list in **Developers** settings at [app.thunderphone.com](https://app.thunderphone.com). Remember that `localhost` is always allowed.
  </Accordion>
</AccordionGroup>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Styling" icon="palette" href="/widget/styling">
    Customize the widget appearance with CSS custom properties.
  </Card>

  <Card title="React Component" icon="react" href="/widget/react">
    Using React? The component integration is simpler.
  </Card>
</CardGroup>
