This guide will walk you through creating a web application that implements ElevenLabs Conversational AI using the @11labs/client SDK. You’ll build a simple interface that allows users to have voice conversations with an AI agent.
Prefer to jump straight into the code? You can find the full example code on GitHub!
Let’s enhance the UI with CSS to make it more user-friendly and visually appealing. We’re using raw CSS here, but you can use alternatives like Tailwind CSS or shadcn/ui based on your preference.
This function requests microphone access from the user. We need this before starting any conversation to ensure we can capture the user’s voice.
URL Retrieval
asyncfunctiongetSignedUrl(){try{const response =awaitfetch('/api/signed-url');if(!response.ok)thrownewError('Failed to get signed URL');const data =await response.json();return data.signedUrl;}catch(error){console.error('Error getting signed URL:', error);throw error;}}
The signed URL contains a short lived token that can be used to start the conversation, without exposing your ElevenLabs API key to your end users.
Signed Url is needed for private agents / non-public agents.
These functions handle the UI updates for connection and speaking states. They provide visual feedback to users about the current state of the conversation.
Conversation Initialization
asyncfunctionstartConversation(){// First, check for microphone permissionconst hasPermission =awaitrequestMicrophonePermission();if(!hasPermission){alert('Microphone permission is required for the conversation.');return;}const signedUrl =awaitgetSignedUrl();//const agentId = await getAgentId(); // You can switch to agentID for public agents conversation =awaitConversation.startSession({signedUrl: signedUrl,//agentId: agentId, // You can switch to agentID for public agentsonConnect:()=>{console.log('Connected');updateStatus(true); startButton.disabled=true; endButton.disabled=false;},onDisconnect:()=>{// Handle disconnectionupdateStatus(false);toggleButtons(false);updateSpeakingStatus({mode:'listening'});},onError:(error)=>{// Handle errors during conversationconsole.error('Conversation error:', error);alert('An error occurred during the conversation.');},onModeChange:(mode)=>{// Update UI when agent switches between speaking and listeningupdateSpeakingStatus(mode);}});}
This function orchestrates the conversation startup process. Conversation initialization can be changed to public agents
by commenting signedUrl and uncommenting agentId
app.get('/api/signed-url',async(req, res)=>{try{const response =awaitfetch(`https://api.elevenlabs.io/v1/convai/conversation/get_signed_url?agent_id=${process.env.AGENT_ID}`,{method:'GET',headers:{'xi-api-key': process.env.XI_API_KEY,}});if(!response.ok){thrownewError('Failed to get signed URL');}const data =await response.json(); res.json({signedUrl: data.signed_url});}catch(error){console.error('Error:', error); res.status(500).json({error:'Failed to get signed URL'});}});
The signed URL is essential because:
It provides secure, temporary access to the conversational API
Keeps your API key secure on the backend
Allows the frontend to connect without exposing sensitive credentials