By Sagar Shankaran, Founder of CallSphere
Wire ElevenLabs Conversational AI to an Express server, expose a public agent over WebSocket, and trigger client tools — full TypeScript tutorial for 2026.
Key takeaways
TL;DR — ElevenLabs Conversational AI gives you a hosted agent with TTS-grade voices and built-in turn-taking. The
@elevenlabs/clientpackage handles the WebSocket; you only write your tool handlers and a thin Express layer for signed URLs.
A small Express service that mints a signed conversation URL, plus a Node.js client that joins the agent, executes registered "client tools" (like get_weather or book_slot), and streams audio. By the end you'll have a working voice loop with one of ElevenLabs' premium voices and tools the agent can call mid-conversation.
agent_id).npm install express @elevenlabs/client elevenlabs node-fetch.ELEVENLABS_API_KEY in env.flowchart LR
Browser -->|GET /signed-url| Express
Express -->|REST| ElevenLabs
Browser -->|WS conversation| ElevenLabs
Browser -->|client_tool calls| ToolHandler
In the dashboard, create an agent, paste a system prompt, pick a voice (e.g., Rachel), and define one client tool with name get_booking_slots and a JSON Schema for params. Copy the agent_id.
For private agents you need a server-signed URL — never ship your API key to the browser.
```ts // server.ts import express from "express"; import fetch from "node-fetch";
const app = express(); app.get("/signed-url", async (_req, res) => { const r = await fetch( `https://api.elevenlabs.io/v1/convai/conversation/get-signed-url?agent_id=${process.env.AGENT_ID}\`, { headers: { "xi-api-key": process.env.ELEVENLABS_API_KEY! } } ); const { signed_url } = await r.json(); res.json({ signed_url }); }); app.listen(3000); ```
```ts // client.ts (bundled to browser) import { Conversation } from "@elevenlabs/client";
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
async function start() { const { signed_url } = await fetch("/signed-url").then(r => r.json());
const conversation = await Conversation.startSession({ signedUrl: signed_url, clientTools: { get_booking_slots: async ({ date }: { date: string }) => { const slots = await fetch(`/api/slots?date=${date}`).then(r => r.json()); return JSON.stringify(slots); }, book_appointment: async ({ slot_id, name }) => { await fetch("/api/book", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ slot_id, name }), }); return "Booked"; }, }, onModeChange: ({ mode }) => console.log("mode:", mode), onStatusChange: ({ status }) => console.log("status:", status), });
document.getElementById("end")!.onclick = () => conversation.endSession(); } start(); ```
If your tool execution belongs server-side (DB writes, secrets), run the agent from Python and stream audio over your own transport:
```python from elevenlabs.client import ElevenLabs from elevenlabs.conversational_ai.conversation import Conversation, ClientTools from elevenlabs.conversational_ai.default_audio_interface import DefaultAudioInterface
client = ElevenLabs(api_key=os.environ["ELEVENLABS_API_KEY"]) tools = ClientTools()
async def get_booking_slots(params): return await db.fetch_slots(params["date"])
tools.register("get_booking_slots", get_booking_slots, is_async=True)
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
conv = Conversation( client=client, agent_id=os.environ["AGENT_ID"], requires_auth=True, audio_interface=DefaultAudioInterface(), client_tools=tools, ) conv.start_session() ```
ElevenLabs has a native Twilio integration: import a Twilio number into the ElevenLabs dashboard, and inbound calls are routed to your agent automatically. For outbound, hit the Twilio outbound endpoint:
```ts await fetch(`https://api.elevenlabs.io/v1/convai/twilio/outbound-call\`, { method: "POST", headers: { "xi-api-key": KEY, "content-type": "application/json" }, body: JSON.stringify({ agent_id: AGENT_ID, agent_phone_number_id: PHONE_ID, to_number: "+18453884261", }), }); ```
Every clientTool invocation is a great audit hook — log the args and the response to your DB so you can replay conversations later.
/signed-url.JSON.stringify the response — the agent reads it as text.CallSphere's Salon vertical runs 4 ElevenLabs Conversational AI agents (booking, rescheduling, FAQ, retention) with GB-YYYYMMDD-### booking references handed back to the agent as tool results. Healthcare uses OpenAI Realtime PCM16 24kHz instead, but the tool registration pattern is identical. Pricing starts at $149/$499/$1499; 14-day trial here.
OpenAI Realtime vs ElevenLabs Conversational AI? ElevenLabs ships with premium voices and a hosted dashboard. OpenAI Realtime is rawer but cheaper and lower-latency for phone.
Can I bring my own LLM? ElevenLabs supports custom LLMs (Claude, GPT-4o) via the agent settings.
Are tool calls billed separately? No — tool execution happens in your code, billing covers only conversation minutes.
How long can a session last? Up to 30 minutes per ElevenLabs limits as of April 2026.
Written by
Sagar Shankaran· Founder, CallSphere
Sagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
Haystack 2.7's Agent component plus an Ollama-served Llama 3.2 gives you tool-calling RAG with citations. Here's a complete pipeline against your own document store.
Run STT, LLM, and TTS entirely on Cloudflare's edge — no OpenAI, no ElevenLabs. Real working code with Whisper, Llama 3.3 70B, and Deepgram Aura.
Version your prompts in git, run a 50-case eval suite on every PR, block merges below threshold, and ship a new agent prompt with confidence — full GitHub Actions tutorial.
AI SDK 5 ships fully typed chat for React, Svelte, Vue, and Angular plus first-class agent loop primitives. Here are the patterns that matter for shipping in 2026.
Replace expensive outbound SDR tooling with a self-hosted dialer that runs OpenAI Realtime agents at 100 concurrent calls. Full architecture and code.
Mastra.ai is becoming the go-to TypeScript agent framework in 2026. Workflows, RAG, evals, and an honest comparison with Vercel AI SDK 5 for serious teams.
© 2026 CallSphere LLC. All rights reserved.
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.
Try Live DemoBook a DemoCalculate Your ROI