SDK de Kotlin

SDK de ElevenAgents: implementa agentes de voz interactivos y personalizados en minutos para aplicaciones Android.

Consulta la descripción general de ElevenAgents para saber cómo funciona ElevenAgents.

Instalación

Añade el SDK de ElevenLabs a tu proyecto de Android incluyendo la siguiente dependencia en el archivo build.gradle de tu app:

build.gradle.kts
dependencies {
// ElevenLabs Agents SDK (Android)
implementation("io.elevenlabs:elevenlabs-android:<latest>")
// Kotlin coroutines, AndroidX, etc., as needed by your app
}

Puedes encontrar un ejemplo de app para Android que usa este SDK aquí

Requisitos

  • Nivel de API de Android 21 (Android 5.0) o superior
  • Permiso de Internet para llamadas a la API
  • Permiso de micrófono para la entrada de voz
  • Configuración de seguridad de red para llamadas HTTPS

Configuración

Configuración del manifiesto

Añade los permisos necesarios a tu AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

Permisos en tiempo de ejecución

Para Android 6.0 (nivel de API 23) y versiones posteriores, debes solicitar permiso de micrófono en tiempo de ejecución:

import android.Manifest
import android.content.pm.PackageManager
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
private fun requestMicrophonePermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) {
// Show explanation to the user
showPermissionExplanationDialog()
} else {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.RECORD_AUDIO),
MICROPHONE_PERMISSION_REQUEST_CODE
)
}
}
}

Uso

Inicializa el SDK de ElevenLabs en tu clase Application o actividad principal:

Inicia una sesión de conversación con una de estas opciones:

  • Agente público: pasa agentId
  • Agente privado: pasa conversationToken aprovisionado desde tu backend (nunca expongas tu clave de API al cliente).
import io.elevenlabs.ConversationClient
import io.elevenlabs.ConversationConfig
import io.elevenlabs.ConversationSession
import io.elevenlabs.ClientTool
import io.elevenlabs.ClientToolResult
// Start a public agent session (token generated for you)
val config = ConversationConfig(
agentId = "<your_public_agent_id>", // OR conversationToken = "<token>"
userId = "your-user-id",
// Optional callbacks
onConnect = { conversationId ->
// Called when the conversation is connected and returns the conversation ID. You can access conversationId via session.getId() too
},
onMessage = { source, messageJson ->
// Raw JSON messages from data channel; useful for logging/telemetry
},
onModeChange = { mode ->
// "speaking" | "listening" — drive UI indicators
},
onStatusChange = { status ->
// "connected" | "connecting" | "disconnected"
},
onCanSendFeedbackChange = { canSend ->
// Enable/disable thumbs up/down buttons for feedback reporting
},
onUnhandledClientToolCall = { call ->
// Agent requested a client tool not registered on the device
},
onVadScore = { score ->
// Voice Activity Detection score, range from 0 to 1 where higher values indicate higher confidence of speech
},
onAudioAlignment = { alignment ->
// Character-level timing data for synchronized text display
val chars = alignment["chars"] as? List<*>
val startTimes = alignment["char_start_times_ms"] as? List<*>
val durations = alignment["char_durations_ms"] as? List<*>
Log.d("ExampleApp", "Audio alignment: $chars")
},
// List of client tools the agent can invoke
clientTools = mapOf(
"logMessage" to object : ClientTool {
override suspend fun execute(parameters: Map<String, Any>): ClientToolResult {
val message = parameters["message"] as? String
Log.d("ExampleApp", "[INFO] Client Tool Log: $message")
return ClientToolResult.success("Message logged successfully")
}
}
),
)
// In an Activity context
val session: ConversationSession = ConversationClient.startSession(config, this)

Ten en cuenta que ElevenAgents requiere acceso al micrófono. Valora explicar y solicitar permisos en la interfaz de tu app antes de iniciar la conversación, especialmente en Android 6.0+ donde los permisos en tiempo de ejecución son obligatorios.

Si una herramienta está configurada con expects_response=false en el servidor, devuelve null desde execute para no enviar el resultado de la herramienta al agente.

Agentes públicos y privados

  • Agentes públicos (sin autenticación): inicialízalos con agentId en ConversationConfig. El SDK solicita un token de conversación a ElevenLabs sin necesidad de una clave de API en el dispositivo.
  • Agentes privados (con autenticación): inicialízalos con conversationToken en ConversationConfig. Tu servidor solicita un token de conversación a ElevenLabs mediante tu clave de API de ElevenLabs.
Nunca incluyas claves de API en los clientes. Se pueden extraer fácilmente y usar de forma maliciosa.

Herramientas de cliente

Registra herramientas de cliente para permitir que el agente llame a funciones locales del dispositivo.

val config = ConversationConfig(
agentId = "<public_agent>",
clientTools = mapOf(
"logMessage" to object : io.elevenlabs.ClientTool {
override suspend fun execute(parameters: Map<String, Any>): io.elevenlabs.ClientToolResult? {
val message = parameters["message"] as? String ?: return io.elevenlabs.ClientToolResult.failure("Missing 'message'")
android.util.Log.d("ClientTool", "Log: $message")
return null // No response needed for fire-and-forget tools
}
}
)
)

Cuando el agente emite una client_tool_call, el SDK ejecuta la herramienta correspondiente y responde con un client_tool_result. Si la herramienta no está registrada, se invoca onUnhandledClientToolCall y se devuelve un resultado de error al agente (si se espera una respuesta).

Descripción general de callbacks

  • onConnect - Se llama cuando se establece la conexión WebRTC. Devuelve el ID de la conversación.
  • onMessage - Se llama cuando se recibe un mensaje nuevo. Puede tratarse de transcripciones provisionales o finales de la voz del usuario, respuestas generadas por el LLM o mensajes de depuración. Proporciona la fuente ("ai" o "user") y el mensaje JSON sin procesar.
  • onModeChange - Se llama cuando cambia el modo de conversación. Es útil para indicar si el agente está hablando ("speaking") o escuchando ("listening").
  • onStatusChange - Se llama cuando cambia el estado de la conversación ("connected", "connecting" o "disconnected").
  • onCanSendFeedbackChange - Se llama cuando cambia la posibilidad de enviar feedback. Activa o desactiva los botones de feedback.
  • onUnhandledClientToolCall - Se llama cuando el agente solicita una herramienta de cliente que no está registrada en el dispositivo.
  • onVadScore - Se llama cuando cambia la puntuación de detección de actividad de voz. El intervalo va de 0 a 1, y los valores más altos indican una mayor confianza de que hay habla.
  • onAudioAlignment - Se llama cuando se reciben datos de alineación de audio y proporciona información de tiempo a nivel de carácter para la voz del agente.

No todos los eventos de cliente están activados de forma predeterminada para un agente. Si has activado un callback pero no recibes eventos, asegúrate de que tu agente de ElevenLabs tiene activado el evento correspondiente. Puedes hacerlo en la pestaña “Advanced” de la configuración del agente en el panel de ElevenLabs.

Métodos

startSession

El método startSession inicia la conexión WebRTC y comienza a usar el micrófono para comunicarse con el agente de ElevenLabs Agents.

Agentes públicos

Para los agentes públicos (es decir, agentes que no tienen la autenticación activada), solo se necesita agentId. Puedes obtener el ID del agente desde la interfaz de ElevenLabs.

val session = ConversationClient.startSession(
config = ConversationConfig(
agentId = "your-agent-id"
),
context = this
)
Agentes privados

Para los agentes privados, debes pasar un conversationToken obtenido mediante la API de ElevenLabs. Para generar este token necesitas una clave de API de ElevenLabs.

El conversationToken es válido durante 10 minutos.
// Server-side token generation (Node.js example)
app.get("/conversation-token", yourAuthMiddleware, async (req, res) => {
const response = await fetch(
`https://api.elevenlabs.io/v1/convai/conversation/token?agent_id=${process.env.AGENT_ID}`,
{
headers: {
// Requesting a conversation token requires your ElevenLabs API key
// Do NOT expose your API key to the client!
"xi-api-key": process.env.ELEVENLABS_API_KEY,
},
}
);
if (!response.ok) {
return res.status(500).send("Failed to get conversation token");
}
const body = await response.json();
res.send(body.token);
});

A continuación, pasa el token al método startSession. Ten en cuenta que para los agentes privados solo se necesita conversationToken.

// Get conversation token from your server
val conversationToken = fetchConversationTokenFromServer()
// For private agents, pass in the conversation token
val session = ConversationClient.startSession(
config = ConversationConfig(
conversationToken = conversationToken
),
context = this
)

Opcionalmente, puedes pasar un ID de usuario para identificarlo en la conversación. Puede ser tu propio identificador de cliente. Se incluirá en los datos de inicio de la conversación enviados al servidor.

val session = ConversationClient.startSession(
config = ConversationConfig(
agentId = "your-agent-id",
userId = "your-user-id"
),
context = this
)

endSession

Método para finalizar manualmente la conversación. Se desconectará y finalizará la conversación.

session.endSession()

sendUserMessage

Envía un mensaje de texto al agente durante una conversación activa. Esto activará una respuesta del agente.

session.sendUserMessage("Hello, how can you help me?")

sendContextualUpdate

Envía información contextual al agente que no activará una respuesta.

session.sendContextualUpdate(
"User navigated to the profile page. Consider this for next response."
)

sendFeedback

Proporciona feedback sobre la calidad de la conversación. Esto ayuda a mejorar el rendimiento del agente. Usa onCanSendFeedbackChange para activar tu interfaz de pulgar arriba/abajo cuando se permita enviar feedback.

// Positive feedback
session.sendFeedback(true)
// Negative feedback
session.sendFeedback(false)

sendUserActivity

Notifica al agente sobre la actividad del usuario para evitar interrupciones. Es útil cuando el usuario está usando activamente la app y el agente debe dejar de hablar, por ejemplo, cuando el usuario está escribiendo en un chat.

El agente dejará de hablar durante unos 2 segundos después de recibir esta señal.

session.sendUserActivity()

getId

Obtén el ID de la conversación.

val conversationId = session.getId()
Log.d("Conversation", "Conversation ID: $conversationId")
// e.g., "conv_123"

Silenciar/activar micrófono

session.toggleMute()
session.setMicMuted(true) // mute
session.setMicMuted(false) // unmute

Observa session.isMuted para actualizar la etiqueta de la interfaz entre “Silenciar” y “Activar micrófono”.

Propiedades

status

Obtén el estado actual de la conversación.

val status = session.status
Log.d("Conversation", "Current status: $status")
// Values: DISCONNECTED, CONNECTING, CONNECTED

ProGuard / R8

Si reduces u ofuscas el código, asegúrate de conservar los modelos de Gson y LiveKit. Reglas de ejemplo (ajústalas según sea necesario):

-keep class io.elevenlabs.** { *; }
-keep class io.livekit.** { *; }
-keepattributes *Annotation*

Solución de problemas

  • Asegúrate de que el permiso de micrófono se concede en tiempo de ejecución
  • Si la reconexión se bloquea, verifica que tu app llama a session.endSession() y que inicias una nueva instancia de sesión antes de volver a conectarte
  • En emuladores, verifica que las rutas de entrada y salida de audio funcionen; los dispositivos físicos suelen comportarse de forma más fiable

Implementación de ejemplo

Para ver una implementación de ejemplo, consulta la app de ejemplo en el repositorio del SDK de ElevenLabs para Android. La app muestra:

  • Conexión y desconexión con un toque
  • Indicador de hablando/escuchando
  • Botones de feedback que se activan o desactivan en la interfaz
  • Indicador de escritura mediante sendUserActivity()
  • Mensajes contextuales y de usuario desde un campo de entrada
  • Botón para silenciar/activar el micrófono