D
DevToolsReview

Build a Voice Agent with Claude Code and ElevenLabs (2026)

ElevenLabs API tutorial: create a voice agent, use Claude as its LLM, embed it with @elevenlabs/react, and build it with Claude Code. Oct 2026 pricing.

DR

DevTools Review

· Updated October 20, 2026
Claude Code

ElevenLabs’ agent platform, now branded ElevenAgents in its docs, gives you a hosted voice agent: speech-to-text, an LLM of your choice, and ElevenLabs text-to-speech, wired together and reachable from a browser over WebRTC. You configure the agent once, then talk to it from your own app through an official SDK.

This ElevenLabs API tutorial walks through the whole path: getting an API key, creating an agent (dashboard or API), connecting it to Claude as the LLM, embedding it in a small React app without leaking your key, and using Claude Code to scaffold and iterate on the code. Every package name, endpoint and price below was checked against ElevenLabs’ official docs and pricing pages on October 20, 2026.

Check the docs for the latest SDK version. The ElevenLabs SDKs ship often. At the time of writing, npm lists @elevenlabs/react 1.18.0, @elevenlabs/client 1.28.0 and @elevenlabs/elevenlabs-js 2.73.0, and a 1.0 release candidate of the conversation packages is on the next tag. If an import below fails, compare it against the React SDK docs.

What You’ll Need

  • An ElevenLabs account. The Free plan includes 15 agent call minutes a month, which is enough to follow this guide. Sign up at elevenlabs.io.
  • Node.js and npm for the React app and the small token server.
  • Claude Code installed and authenticated. If you haven’t set it up, follow our Claude Code setup guide first. Cursor works for the same workflow; the prompts below translate directly.
  • A browser with microphone access. Voice conversations need it.

How the Pieces Fit Together

PieceWhere it livesWhat it does
The agent (prompt, voice, LLM, knowledge base)ElevenLabsConfigured in the dashboard, CLI or API
Your API keyYour server onlyRequests short-lived conversation tokens
@elevenlabs/reactYour frontendOpens the voice session, handles mic and audio
A token endpointYour serverTrades your API key for a per-conversation token

ElevenLabs’ docs are blunt about the key: “Do not share it with others or expose it in any client-side code (browsers, apps).” Everything below is built around that rule.

Step 1: Create an ElevenLabs API Key

Create a key from the API keys page in your dashboard settings (elevenlabs.io/app/settings/api-keys). Every request to the ElevenLabs API sends the key in an xi-api-key header.

A few key options are worth setting before you copy it:

  • Scope restriction. Limit which API endpoints the key can call.
  • Credit quota. Cap how many credits the key can spend, which is useful while you’re experimenting.
  • Expiry. User keys can be set to expire after a preset between 15 minutes and 30 days.
  • IP allowlisting. Restrict the key to public IPs or CIDR ranges once you know where your server runs.

Put the key in a .env file in your project root, using the variable name ElevenLabs’ quickstart uses:

# .env  (never commit this file)
ELEVENLABS_API_KEY=your_api_key_here

If a key is committed to a public GitHub repo, GitHub’s secret scanning lets ElevenLabs disable it automatically. Treat that as a safety net, not a plan.

Step 2: Create Your Agent

You have three official routes. Pick one.

Option A: The dashboard (fastest)

In the ElevenAgents dashboard, create a new agent with the Blank template. On the Agent tab, set a First message (what the agent says when the call starts) and a System prompt (its role and rules). Pick a voice on the Voice tab, then press Test AI agent to talk to it in the browser.

Option B: The API with the JavaScript SDK

Install the server SDK and dotenv:

npm install @elevenlabs/elevenlabs-js
npm install dotenv

Then create createAgent.mts. This follows the official quickstart; the voice ID and TTS model are the ones the docs use:

import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import "dotenv/config";

const elevenlabs = new ElevenLabsClient();

const agent = await elevenlabs.conversationalAi.agents.create({
  name: "My voice agent",
  tags: ["test"],
  conversationConfig: {
    tts: {
      voiceId: "aMSt68OGf4xUZAnLpTU8",
      modelId: "eleven_flash_v2",
    },
    agent: {
      firstMessage: "Hi, how can I help you today?",
      prompt: {
        prompt: "You are a concise, friendly support assistant for a developer tool. Keep answers to a couple of sentences.",
      },
    },
  },
});

console.log(`Agent created with ID: ${agent.agentId}`);

Run it with:

npx tsx createAgent.mts

Save the printed agent ID. You’ll need it in Step 4.

Option C: The ElevenLabs CLI

If you’d rather keep agent configs in your repo as JSON, the CLI does that:

npm install -g @elevenlabs/cli
elevenlabs agents init
elevenlabs auth login
elevenlabs agents add "My Assistant" --template assistant
elevenlabs agents push

elevenlabs auth login authenticates through your browser with OAuth. After a push, the agent ID is recorded in agents.json. This route suits Claude Code well, since the agent’s config becomes a file it can edit.

Step 3: Connect the Agent to Claude (or Another LLM)

The LLM is the agent’s “brain”: it reads the transcribed speech and writes the reply that gets spoken. In the agent’s settings, open the Primary LLM selector and choose a model.

As of October 2026, ElevenLabs’ models page lists Anthropic models including Claude Opus 5.5, Claude Sonnet 5.5, Claude Sonnet 4.6 and Claude Haiku 4.5, alongside OpenAI, Google and ElevenLabs-hosted options. You can also point the agent at your own endpoint through the custom LLM integration.

Some practical notes from the docs:

  • Latency matters more than raw capability for live voice. ElevenLabs recommends choosing a low-latency model and measuring response time with your own prompts and tools. Bigger models mean longer pauses before the agent answers.
  • Reasoning slows turn-taking. The docs suggest starting with a low thinking budget or reasoning effort for live voice agents.
  • Keep backup LLMs on. You can set a fallback sequence for when the primary model fails. ElevenLabs strongly discourages disabling it in production.
  • LLM costs are separate from call minutes. More on that in the pricing section.

Note that the agent’s LLM here is billed through ElevenLabs. It’s separate from the Claude subscription or API key you use for Claude Code itself.

Step 4: Add the Agent to a React App

Scaffold a React app (or let Claude Code do it, see Step 5) and install the React SDK:

npm install @elevenlabs/react

@elevenlabs/react re-exports everything from @elevenlabs/client, so you don’t need both.

Public agent: agent ID only

If your agent doesn’t have authentication enabled, the client only needs the agent ID. This is the minimal example from the React SDK docs:

import {
  ConversationProvider,
  useConversationControls,
  useConversationStatus,
} from "@elevenlabs/react";

function App() {
  return (
    <ConversationProvider>
      <Agent />
    </ConversationProvider>
  );
}

function Agent() {
  const { startSession, endSession } = useConversationControls();
  const { status } = useConversationStatus();

  if (status === "connected") {
    return <button onClick={endSession}>End</button>;
  }

  return (
    <button onClick={() => startSession({ agentId: "your-agent-id" })}>
      Start
    </button>
  );
}

export default App;

All conversation hooks have to sit inside a ConversationProvider. The docs also recommend explaining to the user why you need the microphone before the session starts, then requesting it with navigator.mediaDevices.getUserMedia({ audio: true }).

For a public agent, lock down where it can be used: in the agent’s Security tab, add an allowlist of up to 10 hostnames (for example localhost:3000 and your production domain).

Private agent: a server-issued conversation token

For anything real, enable authentication on the agent and have your server issue a short-lived token. Voice conversations use WebRTC by default, so you request a conversation token. This is a minimal Express endpoint based on the docs:

// server.js
import "dotenv/config";
import express from "express";

const app = express();

app.get("/conversation-token", async (req, res) => {
  // Add your own auth check here before issuing a token.
  const response = await fetch(
    `https://api.elevenlabs.io/v1/convai/conversation/token?agent_id=${process.env.AGENT_ID}`,
    { headers: { "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);
});

app.listen(3001);

On the client, fetch the token and pass it to startSession:

const response = await fetch("/conversation-token");
const conversationToken = await response.text();

await startSession({ conversationToken });

If you need a WebSocket connection instead (text-only conversations use WebSocket by default), the equivalent endpoint is GET /v1/convai/conversation/get-signed-url?agent_id=..., which returns a signed_url you pass as signedUrl. Signed URLs are valid for 15 minutes. The conversation can run longer, but it has to start inside that window.

No React? Use the widget

For a quick test on any page, ElevenLabs provides an embeddable widget. Replace agent-id with your agent’s ID:

<elevenlabs-convai agent-id="agent-id"></elevenlabs-convai>
<script src="https://unpkg.com/@elevenlabs/convai-widget-embed" async type="text/javascript"></script>

Step 5: Use Claude Code to Scaffold and Iterate

The tedious part is the wiring: dev proxy, env loading, mic-permission UI, status states and errors. Claude Code handles that well if you give it context and keep the key away from it.

Give Claude Code the ElevenLabs context

ElevenLabs publishes two things aimed at AI coding assistants:

  • An agents skill. The quickstart suggests installing it with npx skills add elevenlabs/skills --skill agents.
  • CLI-generated skills. Running elevenlabs generate-skills in your project writes a SKILL.md for every CLI command group into skills/. It needs no API key and matches the CLI version you have installed.

There’s also a hosted MCP server at https://api.elevenlabs.io/v1/mcp that lets an assistant create and manage agents in your workspace through OAuth, with no API key copied into the client. ElevenLabs documents connecting it from Claude Desktop. In Claude Code, the usual way to add a remote MCP server is claude mcp add --transport http elevenlabs https://api.elevenlabs.io/v1/mcp, followed by /mcp to sign in.

Add a CLAUDE.md section

Put the rules where Claude Code reads them every session:

## Voice agent (ElevenLabs)

- Frontend: React + Vite. Voice via `@elevenlabs/react` only
  (ConversationProvider, useConversationControls, useConversationStatus).
- Backend: `server.js` (Express) exposes GET /conversation-token.
  It calls https://api.elevenlabs.io/v1/convai/conversation/token
  with the `xi-api-key` header.
- Secrets: ELEVENLABS_API_KEY and AGENT_ID live in `.env` only.
  Never read, print, or commit `.env`. Never put the API key in
  frontend code or in any VITE_-prefixed variable.
- Agent config (prompt, voice, LLM) is managed in the ElevenLabs
  dashboard / CLI, not hardcoded in the app.
- When unsure about an SDK method, check the installed package's
  types in node_modules/@elevenlabs before guessing.

The VITE_ line matters. Vite exposes any VITE_-prefixed variable to the browser bundle, which is exactly where the key must never go.

Keep the API key out of the repo (and out of Claude’s context)

  1. Add .env to .gitignore before creating it.
  2. Commit a .env.example with empty values so the variable names are documented.
  3. Block Claude Code from reading the real file. Claude Code supports permission deny rules in .claude/settings.json:
{
  "permissions": {
    "deny": ["Read(./.env)", "Read(./.env.*)"]
  }
}
  1. Use a scoped key with a credit quota for development, and a separate key for production.

Prompts that work

Start Claude Code in your project with claude, then build in small steps. One task per prompt keeps the diffs reviewable:

> Scaffold a React + TypeScript app with Vite in this folder. Add an
  Express server in server.js on port 3001 and configure the Vite dev
  proxy so /conversation-token goes to it. Follow CLAUDE.md.
> Implement GET /conversation-token in server.js exactly as described
  in CLAUDE.md. Read ELEVENLABS_API_KEY and AGENT_ID from process.env.
  Return a 500 with a clear message if either is missing.
> Build a VoiceAgent component: explain why we need the microphone,
  request it with getUserMedia, then fetch a conversation token and call
  startSession({ conversationToken }). Show status (disconnected,
  connecting, connected) and an End button.
> Use useConversationMode to show whether the agent is speaking or
  listening, and add a mute toggle with useConversationInput.
> Grep the repo and the built frontend bundle for "xi-api-key" and
  ELEVENLABS_API_KEY. Confirm they appear only in server.js and .env.example.

That last prompt is worth running before every deploy. When you’re ready to ship, our guide to deploying an app built with AI covers hosting the frontend and token server together.

Try Claude Code

ElevenLabs Pricing (October 2026)

ElevenLabs bills two ways. Its general plans give you a monthly credit pool shared across products like text-to-speech and speech-to-text. The agent platform lists call minutes per plan. These figures come from elevenlabs.io/pricing and the agents pricing page, checked October 20, 2026. Prices exclude taxes.

PlanMonthly priceCredits/monthAgent call minutesConcurrent calls
Free$010,000154
Starter$630,000756
Creator$22 ($11 first month)121,00027510
Pro$99600,0001,23820
Scale$2991,800,0003,73830
Business$9906,000,00012,37540
EnterpriseCustomCustomCustomElevated

How agent usage is billed:

  • Additional call minutes: $0.08 per minute.
  • Burst pricing: $0.16 per minute (double) for calls above your concurrency limit, when burst is enabled. It allows up to 3x your normal concurrency.
  • Text messages: $0.003 each. Text messages are listed as a Starter feature, not Free.
  • LLM usage is extra. It’s billed on top of minutes and deducted from your credits. ElevenLabs says it passes third-party LLM costs through at the provider’s published rate, with no markup. It also notes that select Gemini and Claude models follow Vertex AI regional pricing, which is 10% higher on tokens. Each agent’s Primary LLM selector has a Detailed costs view with estimates.
  • Silence is discounted. Voice calls are charged by connection duration, with a 95% discount for silences longer than 10 seconds. Billing runs from when the call connects until it ends or the window closes, so the billed time can be longer than the actual conversation.
  • Telephony has no extra ElevenLabs fee. If you connect Twilio or a SIP trunk, that provider bills you directly.
  • Unlimited agents. There’s no limit on how many agents you can create on any plan.

For text-to-speech outside agents, credits are charged per character. On V2 Multilingual models, 1 character is 1 credit. Flash/Turbo models cost between 0.5 and 1 credit per character via the API. Annual billing equals 10 months of the monthly price. Unused credits roll over for up to two months on paid plans, but not on Free.

What the free tier is good for: building and testing. You get 15 call minutes and 10,000 credits a month. The commercial license starts at Starter, so plan on at least $6 a month before real users talk to your agent. Also note that you need Pay As You Go credits before you can go past your plan’s quota.

Your Claude Code costs are separate. See our Claude Code pricing breakdown for those.

Common Issues

The agent connects but can’t hear me? The browser needs microphone permission, and most browsers only grant it on localhost or HTTPS. Request it with getUserMedia after a user click, and explain why first.

Hook errors on render? Every conversation hook (useConversationControls, useConversationStatus and the rest) has to render inside ConversationProvider. Wrap your app or the relevant subtree.

Token endpoint returns 500? Check that ELEVENLABS_API_KEY and AGENT_ID load on the server (not in the browser). Also check that the key’s scope allows the ElevenAgents endpoints and that it hasn’t hit its credit quota or expiry. An expired key gets a 401, and a request from a non-allowlisted IP gets a 403.

The connection is rejected from my domain? If you use an allowlist, add every hostname you serve from, including localhost:3000 (or your dev port) for local work.

Imports fail after an upgrade? The React SDK API changed around ConversationProvider and the granular hooks. ElevenLabs offers a migration skill (npx skills add elevenlabs/packages) for AI coding agents. Compare your code against the current React SDK docs.

Long pauses before the agent replies? Try a faster LLM, lower the reasoning budget, and keep the system prompt and knowledge base lean.

Frequently Asked Questions

Can I use Claude as the LLM for an ElevenLabs voice agent?

Yes. ElevenLabs’ supported models list includes Anthropic models such as Claude Opus 5.5, Claude Sonnet 5.5, Claude Sonnet 4.6 and Claude Haiku 4.5. You choose one in the agent’s Primary LLM setting. LLM usage is billed through ElevenLabs on top of call minutes.

Is the ElevenLabs API free to use?

There’s a Free plan with 10,000 credits and 15 agent call minutes per month, enough for building and testing. The commercial license starts on the $6/month Starter plan.

How much does an ElevenLabs voice agent cost per minute?

Each plan includes call minutes (15 on Free up to 12,375 on Business). Extra minutes cost $0.08 each, or $0.16 under burst pricing. LLM costs are billed separately, and silences longer than 10 seconds get a 95% discount.

Which npm package do I use to add an ElevenLabs agent to a web app?

Use @elevenlabs/react for React apps or @elevenlabs/client for plain JavaScript. Use @elevenlabs/elevenlabs-js on the server to manage agents through the API. The old @11labs/react package is no longer maintained.

How do I keep my ElevenLabs API key out of the browser?

Keep the key on your server and expose an endpoint that requests a conversation token (WebRTC) or signed URL (WebSocket) from ElevenLabs. The browser only ever receives that short-lived credential.

Next Steps

Try Claude Code
DR

Written by DevTools Review

We're developers who use AI coding tools every day. Our reviews are based on real-world experience, not press releases. We test with real projects and share what we actually find.

Change tracker

Get told when a price changes

We re-check what all 11 tools cost against each vendor's own pricing page. When something moves — a price, a free tier, a limit — you get an email. When nothing moves, you get nothing.

Confirmation required. Unsubscribe in one click, from any email.