> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# Guía de inicio rápido de Speech Engine

Esta guía te explica cómo crear un agente con voz mediante Speech Engine. Configurarás un servidor que conecta tu LLM con ElevenLabs y, después, un cliente de navegador para que usuarios puedan mantener conversaciones por voz con tu agente.

> **Tip**
>
> Usa la [skill de ElevenLabs Speech Engine](https://github.com/elevenlabs/skills/tree/main/speech-engine) para añadir voz a tu agente de chat:
>
> ```bash
> npx skills add elevenlabs/skills --skill speech-engine
> ```

## Cómo funciona Speech Engine

Speech Engine conecta tu LLM con ElevenLabs para que usuarios puedan hablar con tu agente y escuchar sus respuestas. ElevenLabs se encarga de Voz a Texto y Texto a Voz; tu servidor proporciona la lógica del LLM.

```mermaid
sequenceDiagram
    participant Browser
    participant ElevenLabs

    box Your Server
        participant SDK as Speech Engine SDK
        participant LLM
    end

    Browser->>ElevenLabs: User speaks (audio)
    ElevenLabs->>SDK: Transcript (WebSocket)
    SDK->>LLM: Conversation history
    LLM->>SDK: Streamed response
    SDK->>ElevenLabs: Text chunks
    ElevenLabs->>Browser: Agent speaks (audio)
```

Cada conexión WebSocket representa una conversación. Cuando el usuario habla, ElevenLabs transcribe el audio y envía la transcripción a tu servidor. Tu servidor la pasa a tu LLM y, después, transmite la respuesta de vuelta. ElevenLabs convierte el texto en voz y lo reproduce en el navegador. El SDK gestiona las conexiones, los turnos de conversación y la detección de interrupciones.

## Requisitos previos

Este tutorial usa la API de OpenAI para el LLM. Necesitas una clave de API de OpenAI configurada en la variable de entorno `OPENAI_API_KEY`.

## Configuración del servidor

#### Crea una clave de API

[Crea una clave de API en el panel aquí](https://elevenlabs.io/app/settings/api-keys), que usarás para [acceder a la API](/docs/es/api-reference/authentication) de forma segura.

Guarda la clave como un secreto gestionado y pásala a los SDK como variable de entorno mediante un archivo `.env` o directamente en la configuración de tu aplicación, según prefieras.

**`.env`**

```js title=".env"
ELEVENLABS_API_KEY=<your_api_key_here>
```

#### Instala las dependencias

```python
pip install elevenlabs openai python-dotenv
```

```typescript
npm install @elevenlabs/elevenlabs-js openai
```

#### Expón el servidor

Speech Engine necesita una URL accesible públicamente. Usa [ngrok](https://ngrok.com) para exponer tu servidor local. El servidor aún no está creado, pero ngrok debe estar ejecutándose primero para que tengas la URL para el siguiente paso.

```bash
ngrok http 3001
```

Copia la URL de reenvío (por ejemplo, `https://abc123.ngrok.io`).

#### Crea una instancia de Speech Engine

Usa el SDK para crear una instancia de Speech Engine y pasa tu URL de ngrok con la ruta `/ws` añadida como URL de WebSocket.

**`create_engine.py`**

```python title="create_engine.py"
import asyncio
from dotenv import load_dotenv
from elevenlabs import AsyncElevenLabs

load_dotenv()

elevenlabs = AsyncElevenLabs(
    api_key=os.getenv("ELEVENLABS_API_KEY"),
)


async def main():
    engine = await elevenlabs.speech_engine.create(
        name="My Speech Engine",
        speech_engine={
            # Note we use the wss protocol instead of https
            "ws_url": "wss://abc123.ngrok.io/ws",
        },
    )

    print(f"Speech Engine ID: {engine.engine_id}")


if __name__ == "__main__":
    asyncio.run(main())
```

**`create-engine.mts`**

```typescript title="create-engine.mts"
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import "dotenv/config";

const elevenlabs = new ElevenLabsClient({
  apiKey: process.env.ELEVENLABS_API_KEY,
});

const engine = await elevenlabs.speechEngine.create({
  name: "My Speech Engine",
  speechEngine: {
    // Note we use the wss protocol instead of https
    wsUrl: "wss://abc123.ngrok.io/ws",
  },
});

console.log("Speech Engine ID:", engine.engineId);
```

Ejecuta este script y copia el ID de Speech Engine (por ejemplo, `seng_8k3m9xr4hjnfg983brhmhkd98n6`) para el siguiente paso.

#### Crea el servidor

Crea un archivo llamado `server.py` o `server.mts` con el siguiente contenido. Esto configura un servidor, conecta Speech Engine en la ruta `/ws` y usa OpenAI para generar respuestas.

**`server.py`**

```python maxLines=0 title="server.py"
import asyncio
import os

from dotenv import load_dotenv
from openai import AsyncOpenAI
from elevenlabs import AsyncElevenLabs

load_dotenv()

# Replace with your Speech Engine ID from step 4
SPEECH_ENGINE_ID = "seng_8k3m9xr4hjnfg983brhmhkd98n6"

openai = AsyncOpenAI(
  api_key=os.getenv("OPENAI_API_KEY"),
)
elevenlabs = AsyncElevenLabs(
  api_key=os.getenv("ELEVENLABS_API_KEY"),
)


def on_init(conversation_id, session):
    print(f"Session started: {conversation_id}")


async def on_transcript(transcript, session):
    stream = await openai.responses.create(
        model="gpt-4o",
        instructions="You are a helpful voice assistant. Keep responses concise and conversational.",
        input=[
            {"role": "assistant" if m.role == "agent" else m.role, "content": m.content}
            for m in transcript
        ],
        stream=True,
    )

    await session.send_response(stream)


def on_close(session):
    print(f"Session ended: {session.conversation_id}")


def on_error(err, session):
    print(f"Error: {err}")


async def main():
    engine = await elevenlabs.speech_engine.get(SPEECH_ENGINE_ID)

    await engine.serve(
        port=3001,
        path="/ws",
        debug=True,
        on_init=on_init,
        on_transcript=on_transcript,
        on_close=on_close,
        on_error=on_error,
    )


if __name__ == "__main__":
    asyncio.run(main())
```

**`server.mts`**

```typescript maxLines=0 title="server.mts"
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import { createServer } from "node:http";
import OpenAI from "openai";
import "dotenv/config";

// Replace with your Speech Engine ID from step 4
const SPEECH_ENGINE_ID = "seng_8k3m9xr4hjnfg983brhmhkd98n6";

const elevenlabs = new ElevenLabsClient({
  apiKey: process.env.ELEVENLABS_API_KEY,
});
const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

const httpServer = createServer();

await elevenlabs.speechEngine.attach(SPEECH_ENGINE_ID, httpServer, "/ws", {
  debug: true,

  onInit(conversationId) {
    console.log("Session started:", conversationId);
  },

  async onTranscript(transcript, signal, session) {
    const response = await openai.responses.create(
      {
        model: "gpt-4o",
        instructions:
          "You are a helpful voice assistant. Keep responses concise and conversational.",
        input: transcript.map((m) => ({
          role: m.role === "agent" ? "assistant" : m.role,
          content: m.content,
        })),
        stream: true,
      },
      { signal },
    );

    session.sendResponse(response);
  },

  onClose(session) {
    console.log("Session ended:", session.conversationId);
  },

  onError(err) {
    console.error("Error:", err);
  },
});

httpServer.listen(3001, () => {
  console.log("Speech Engine server listening on port 3001");
});
```

El callback `onTranscript` / `on_transcript` recibe el historial completo de la conversación y la sesión actual. El SDK de TypeScript también proporciona un `AbortSignal` que se activa si el usuario interrumpe durante una respuesta. Pasar `signal` a la llamada de OpenAI cancela automáticamente la solicitud al LLM cuando hay una interrupción.

`sendResponse()` / `send_response()` acepta una cadena, un iterable asíncrono o un stream de OpenAI, Anthropic o Google Gemini. El SDK extrae automáticamente el contenido de texto.

> **Warning**
>
> En el ejemplo anterior, la transcripción completa del usuario se pasa al LLM. En un entorno de producción, debes añadir medidas de protección para evitar intentos de inyección o manipulación de prompts.

#### Inicia el servidor

```python
python server.py
```

```typescript
npx tsx server.mts
```

## Configuración del cliente

#### Instala el SDK de cliente

#### React

```bash
npm install @elevenlabs/react
```

#### JavaScript

```bash
npm install @elevenlabs/client
```

#### Crea una ruta de token

Añade una ruta en el servidor que genere un token de conversación. Así mantienes tu clave de API fuera del navegador y usas WebRTC para obtener la mejor calidad de audio.

**`token_server.py`**

```python title="token_server.py"
import os

from dotenv import load_dotenv
from flask import Flask, jsonify
from elevenlabs import ElevenLabs

load_dotenv()

app = Flask(__name__)
elevenlabs = ElevenLabs(
    api_key=os.getenv("ELEVENLABS_API_KEY"),
)


@app.route("/api/token")
def get_token():
    # Replace with your Speech Engine ID from step 4 of the server setup
    speech_engine_id = "seng_8k3m9xr4hjnfg983brhmhkd98n6"

    response = elevenlabs.conversational_ai.conversations.get_webrtc_token(
        agent_id=speech_engine_id,
    )

    return jsonify(token=response.token)


if __name__ == "__main__":
    app.run(port=3002)
```

**`token-server.mts`**

```typescript title="token-server.mts"
import express from "express";
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import "dotenv/config";

const app = express();
const elevenlabs = new ElevenLabsClient({
  apiKey: process.env.ELEVENLABS_API_KEY,
});

app.get("/api/token", async (req, res) => {
  // Replace with your Speech Engine ID from step 4 of the server setup
  const speechEngineId = "seng_8k3m9xr4hjnfg983brhmhkd98n6";

  const response = await elevenlabs.conversationalAi.conversations.getWebrtcToken({
    agentId: speechEngineId,
  });

  res.json({ token: response.token });
});

app.listen(3002, () => {
  console.log("Token server listening on port 3002");
});
```

#### Crea la interfaz de conversación

Obtén el token de conversación de tu servidor y úsalo para iniciar una sesión.

#### React

**`App.tsx`**

```tsx title="App.tsx"
import { useConversation } from "@elevenlabs/react";
import { useCallback } from "react";

async function getToken(): Promise<string> {
  const response = await fetch("/api/token");
  if (!response.ok) {
    throw Error("Failed to get conversation token");
  }
  const data = await response.json();
  return data.token;
}

export default function App() {
  const conversation = useConversation({
    onConnect: () => console.log("Connected"),
    onDisconnect: () => console.log("Disconnected"),
    onError: (error: Error) => console.error("Error:", error),
  });

  const startConversation = useCallback(async () => {
    await navigator.mediaDevices.getUserMedia({ audio: true });
    const token = await getToken();
    await conversation.startSession({ conversationToken: token });
  }, [conversation]);

  const stopConversation = useCallback(async () => {
    await conversation.endSession();
  }, [conversation]);

  return (
    <div>
      <p>Status: {conversation.status}</p>
      <button onClick={startConversation} disabled={conversation.status === "connected"}>
        Start conversation
      </button>
      <button onClick={stopConversation} disabled={conversation.status !== "connected"}>
        End conversation
      </button>
    </div>
  );
}
```

#### JavaScript

**`main.ts`**

```typescript title="main.ts"
import { Conversation } from "@elevenlabs/client";

let conversation: Conversation | null = null;

async function getToken(): Promise<string> {
  const response = await fetch("/api/token");
  if (!response.ok) throw Error("Failed to get conversation token");
  const data = await response.json();
  return data.token;
}

document.getElementById("start")!.addEventListener("click", async () => {
  await navigator.mediaDevices.getUserMedia({ audio: true });
  const token = await getToken();

  conversation = await Conversation.startSession({
    conversationToken: token,
    onConnect: () => {
      document.getElementById("status")!.textContent = "Connected";
      (document.getElementById("start") as HTMLButtonElement).disabled = true;
      (document.getElementById("stop") as HTMLButtonElement).disabled = false;
    },
    onDisconnect: () => {
      document.getElementById("status")!.textContent = "Disconnected";
      (document.getElementById("start") as HTMLButtonElement).disabled = false;
      (document.getElementById("stop") as HTMLButtonElement).disabled = true;
    },
    onError: (error) => console.error("Error:", error),
  });
});

document.getElementById("stop")!.addEventListener("click", () => {
  if (conversation) conversation.endSession();
});
```

#### Pruébalo

Asegúrate de que haya tres procesos en ejecución:

1. **ngrok**: reenvío al puerto 3001
2. **Tu servidor de Speech Engine**: `python server.py` o `npx tsx server.mts`
3. **El servidor de tokens**: `npx tsx token-server.mts` o `python token_server.py`

Abre tu aplicación cliente en el navegador y haz clic en **Iniciar conversación**. Concede acceso al micrófono cuando se te solicite y, después, habla. Deberías escuchar la respuesta del agente por los altavoces.

Si tienes `debug: true` activado en el servidor, verás en la consola las transcripciones entrantes y las respuestas salientes.

## Eventos de sesión

| Evento            | Callback de TypeScript | Callback de Python | Descripción                                                                                              |
| ----------------- | ---------------------- | ------------------ | -------------------------------------------------------------------------------------------------------- |
| `user_transcript` | `onTranscript`         | `on_transcript`    | Voz del usuario transcrita. Incluye el historial completo de la conversación y una señal de cancelación. |
| `init`            | `onInit`               | `on_init`          | Sesión inicializada con un ID de conversación.                                                           |
| `close`           | `onClose`              | `on_close`         | Desconexión limpia de ElevenLabs.                                                                        |
| `disconnected`    | `onDisconnect`         | `on_disconnect`    | WebSocket se desconectó inesperadamente.                                                                 |
| `error`           | `onError`              | `on_error`         | Error de protocolo o WebSocket.                                                                          |

## Configurar el primer mensaje del agente

De forma predeterminada, el agente espera a que el usuario hable primero. Para que el agente salude al usuario cuando empiece la conversación, configura un primer mensaje en la opción `overrides` del cliente al iniciar la sesión.

Para permitir que el agente hable primero, debemos actualizar el recurso de Speech Engine para permitir configurarlo desde el cliente.

```python
engine = await elevenlabs.speech_engine.update(
    speech_engine_id="seng_8k3m9xr4hjnfg983brhmhkd98n6",
    overrides={
      "first_message": True,
    },
)
```

```typescript
const engine = await elevenlabs.speechEngine.update("seng_8k3m9xr4hjnfg983brhmhkd98n6", {
  overrides: {
    firstMessage: true,
  },
});
```

A continuación, configuramos el primer mensaje en el SDK de cliente.

#### React

```tsx
conversation.startSession({
  conversationToken: token,
  overrides: {
    agent: {
      firstMessage: "Hello! How can I help you today?",
    },
  },
});
```

#### JavaScript

```typescript
const conversation = await Conversation.startSession({
  conversationToken: token,
  overrides: {
    agent: {
      firstMessage: "Hello! How can I help you today?",
    },
  },
});
```

El agente pronuncia el primer mensaje en cuanto se establece la conexión. No activa el callback `onTranscript` de tu servidor; se gestiona íntegramente en ElevenLabs.

## Configurar la duración máxima de la conversación

De forma predeterminada, una conversación termina tras 600 segundos (10 minutos). Speech Engine admite conversaciones de hasta 2400 segundos (40 minutos). Para permitir conversaciones más largas, actualiza el recurso de Speech Engine con un valor `max_duration_seconds` más alto:

```python
engine = await elevenlabs.speech_engine.update(
    speech_engine_id="seng_8k3m9xr4hjnfg983brhmhkd98n6",
    conversation={
        "max_duration_seconds": 2400,
    },
)
```

```typescript
const engine = await elevenlabs.speechEngine.update("seng_8k3m9xr4hjnfg983brhmhkd98n6", {
  conversation: {
    maxDurationSeconds: 2400,
  },
});
```

También puedes configurarlo en el panel de control: abre tu Speech Engine, ve a la pestaña **Avanzado** y configura **Duración máxima de la conversación** en **Comportamiento conversacional**.

> **Warning**
>
> No se admiten conversaciones de más de 40 minutos y pueden desconectarse, aunque
> `max_duration_seconds` tenga un valor superior. Si necesitas conversaciones más largas, [contacta con nuestro equipo](https://elevenlabs.io/contact-sales).

## Próximos pasos

#### [Referencia del SDK de JavaScript](/docs/es/eleven-api/resources/libraries/speech-engine/javascript-sdk-reference)

Clases, métodos y eventos del SDK de JavaScript.

#### [Referencia del SDK de Python](/docs/es/eleven-api/resources/libraries/speech-engine/python-sdk-reference)

Clases, métodos y eventos del SDK de Python.

#### [Referencia de la API](/docs/es/api-reference/speech-engine/create)

Explora todos los parámetros y formatos de respuesta de Speech Engine.

#### [Aplicación de ejemplo de Next.js](https://github.com/elevenlabs/examples/tree/main/speech-engine/nextjs/quickstart)

Ejecuta localmente una aplicación completa de inicio rápido de Speech Engine.