---
title: "CDN / Etiqueta de script"
description: "Agrega el widget de voz de ThunderPhone a cualquier sitio web sin un empaquetador"
---

La compilación de CDN incluye React internamente, por lo que puedes usar el widget en sitios estáticos, WordPress, Webflow o cualquier página donde puedas agregar HTML. No se requiere npm, empaquetador ni framework.

## URL de CDN

<CodeGroup>
```html Latest (recommended)
<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
<!-- 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>
  Las URL de `latest` son las que genera el generador de fragmentos de inserción del panel. Se almacenan en caché durante 5 minutos e incorporan automáticamente nuevas versiones estables. Las rutas fijadas `/widget/vX.Y.Z/` son inmutables y se almacenan en caché a largo plazo -- si fijas una versión, haz que coincida con la versión de npm de `@thunderphone/widget` con la que desarrollas.
</Tip>

---

## Uso básico

```html
<!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>
```

El widget se monta en el elemento de destino y se muestra como una **barra de posición fija en una esquina de la ventana gráfica** (el valor predeterminado es `bottom-right`, controlado por la opción `position`). El elemento de montaje solo es la raíz de React -- su ubicación en la página no afecta dónde aparece el widget.

---

## Opciones de montaje

`ThunderPhone.mount()` acepta las mismas opciones que el componente de React, además de la propiedad `element`:

| Opción | Tipo | Obligatorio | Predeterminado | Descripción |
|--------|------|-------------|----------------|-------------|
| `element` | `string \| HTMLElement` | Sí | -- | Selector CSS (p. ej., `'#thunderphone'`) o referencia a un elemento del DOM. |
| `publishableKey` | `string` | Sí | -- | Clave de API publicable (`pk_live_...`). El agente se resuelve automáticamente a partir de la configuración del widget de la clave. |
| `theme` | `'light' \| 'dark'` | No | `'light'` | Esquema de color. |
| `primaryColor` | `string` | No | `'#000000'` (claro) / `'#ffffff'` (oscuro) | Cadena de color CSS utilizada como color de acento. |
| `title` | `string` | No | `'Voice assistant'` | Texto mostrado en la barra del widget. |
| `position` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | No | `'bottom-right'` | Posición fija en la ventana gráfica. |
| `apiBase` | `string` | No | `'https://api.thunderphone.com/v1'` | Anulación de la URL base de la API. |
| `language` | `string` | No | -- | Anulación de idioma por sesión -- un código de idioma o configuración regional como `en`, `es` o `fr-FR`. Si no se establece, se aplica el idioma configurado del agente. Requiere `language` en las [anulaciones del navegador](/es/guides/embed-web-widget) de la clave. |
| `voice` | `string` | No | -- | Anulación de voz por sesión -- un nombre de voz como `maria`. Si no se establece, se aplica la voz configurada del agente. Requiere `voice` en las [anulaciones del navegador](/es/guides/embed-web-widget) de la clave. |
| `context` | `string` | No | -- | Contexto factual de la página o el sitio por sesión que se pasa al agente. Se trunca del lado del servidor a 12,000 caracteres. Requiere `context` en las [anulaciones del navegador](/es/guides/embed-web-widget) de la clave. |
| `onConnect` | `() => void` | No | -- | Se llama cuando se conecta la sesión de voz. |
| `onDisconnect` | `() => void` | No | -- | Se llama cuando termina la sesión. |
| `onError` | `(error) => void` | No | -- | Se llama cuando ocurren errores. El error tiene los campos `error` (código) y `message`. |
| `ringtone` | `boolean \| string` | No | `false` | Reproduce un tono de llamada mientras se conecta. `true` para el tono predeterminado o una cadena de URL para audio personalizado. |

---

## Limpieza

`ThunderPhone.mount()` devuelve una instancia del widget con métodos de limpieza:

```html
<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>
```

Tanto `unmount()` como `destroy()` hacen lo mismo -- desconectan cualquier sesión de voz activa y eliminan el widget del DOM. Usa el que se lea mejor en tu código.

<Note>
  Siempre limpia el widget al navegar fuera de aplicaciones de una sola página o cuando se elimina el elemento contenedor. Esto garantiza que las sesiones de voz activas se desconecten correctamente.
</Note>

---

## Ejemplos

### Tema oscuro con color personalizado

```html
<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>
```

### Posición personalizada

```html
<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>
```

### Con callbacks de eventos

```html
<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>
```

### Con tono de llamada

```html
<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>
```

### Uso de una referencia a un elemento DOM

```html
<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>
```

### Integración con WordPress / CMS

Agrega esto a un bloque de HTML personalizado o al pie de página de tu tema:

```html
<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>
  El `<div>` puede ir en cualquier lugar -- solo es el punto de montaje. El widget se muestra como una superposición fija en una esquina de la ventana gráfica (definida por la opción `position`) y no fluye con el contenido circundante.
</Tip>

---

## Solución de problemas

<AccordionGroup>
  <Accordion title="El widget no aparece">
    Asegúrate de que se carguen los archivos CSS y JS. Revisa la consola del navegador para detectar errores de red. Verifica que el elemento de destino exista en el DOM antes de llamar a `ThunderPhone.mount()`.
  </Accordion>

  <Accordion title="ThunderPhone no está definido">
    El script aún no se ha cargado. Asegúrate de que la etiqueta `<script>` para `widget.js` aparezca antes de tu llamada de montaje, o envuelve la llamada de montaje en un listener de `DOMContentLoaded`.
  </Accordion>

  <Accordion title="Error de dominio no permitido">
    Agrega tu dominio a los dominios permitidos del widget en la página **Widgets web** de [app.thunderphone.com](https://app.thunderphone.com). `localhost` no se permite automáticamente en producción; agrégalo a la lista para realizar pruebas locales.
  </Accordion>
</AccordionGroup>

---

## Próximos pasos

<CardGroup cols={2}>
  <Card title="Estilos" icon="palette" href="/es/widget/styling">
    Personaliza la apariencia del widget con propiedades personalizadas de CSS.
  </Card>
  <Card title="Componente de React" icon="react" href="/es/widget/react">
    ¿Usas React? La integración del componente es más sencilla.
  </Card>
</CardGroup>
