{"id":"elevenlabs","kind":"sdk","name":"ElevenLabs","slug":"elevenlabs","description":"Voice AI SDKs: text-to-speech, speech-to-text, agents, and audio tools.","vendor":"ElevenLabs","languages":["python","typescript","javascript"],"categories":["ai","media","comms"],"homepage":"https://elevenlabs.io","docsUrl":"https://elevenlabs.io/docs","githubUrl":"https://github.com/elevenlabs/skills","packages":[{"registry":"npm","name":"@elevenlabs/elevenlabs-js","url":"https://www.npmjs.com/package/@elevenlabs/elevenlabs-js"},{"registry":"pypi","name":"elevenlabs","url":"https://pypi.org/project/elevenlabs/"}],"tags":["tts","stt","voice"],"skills":[{"name":"text-to-speech","url":"https://github.com/elevenlabs/skills/tree/main/text-to-speech","install":"npx skills add elevenlabs/skills --skill text-to-speech","sdk":"elevenlabs","key":"elevenlabs/text-to-speech","description":"Convert text to speech using ElevenLabs voice AI. Use when generating audio from text, creating voiceovers, building voice apps, or synthesizing speech in 70+ languages.","hasContent":true,"content":"---\nname: text-to-speech\ndescription: Convert text to speech using ElevenLabs voice AI. Use when generating audio from text, creating voiceovers, building voice apps, or synthesizing speech in 70+ languages.\nlicense: MIT\ncompatibility: Requires internet access and an ElevenLabs API key (ELEVENLABS_API_KEY).\nmetadata: {\"openclaw\": {\"requires\": {\"env\": [\"ELEVENLABS_API_KEY\"]}, \"primaryEnv\": \"ELEVENLABS_API_KEY\"}}\n---\n\n# ElevenLabs Text-to-Speech\n\nGenerate natural speech from text - supports 70+ languages, multiple models for quality vs latency tradeoffs.\n\n> **Setup:** See [Installation Guide](references/installation.md). For JavaScript, use `@elevenlabs/*` packages only.\n\n## Quick Start\n\n### Python\n\n```python\nfrom elevenlabs import ElevenLabs\n\nclient = ElevenLabs()\n\naudio = client.text_to_speech.convert(\n    text=\"Hello, welcome to ElevenLabs!\",\n    voice_id=\"JBFqnCBsd6RMkjVDRZzb\",  # George\n    model_id=\"eleven_multilingual_v2\"\n)\n\nwith open(\"output.mp3\", \"wb\") as f:\n    for chunk in audio:\n        f.write(chunk)\n```\n\n### JavaScript\n\n```javascript\nimport { ElevenLabsClient } from \"@elevenlabs/elevenlabs-js\";\nimport { createWriteStream } from \"fs\";\n\nconst client = new ElevenLabsClient();\nconst audio = await client.textToSpeech.convert(\"JBFqnCBsd6RMkjVDRZzb\", {\n  text: \"Hello, welcome to ElevenLabs!\",\n  modelId: \"eleven_multilingual_v2\",\n});\naudio.pipe(createWriteStream(\"output.mp3\"));\n```\n\n### cURL\n\n```bash\ncurl -X POST \"https://api.elevenlabs.io/v1/text-to-speech/JBFqnCBsd6RMkjVDRZzb\" \\\n  -H \"xi-api-key: $ELEVENLABS_API_KEY\" -H \"Content-Type: application/json\" \\\n  -d '{\"text\": \"Hello!\", \"model_id\": \"eleven_multilingual_v2\"}' --output output.mp3\n```\n\n## Models\n\n| Model ID | Languages | Latency | Best For |\n|----------|-----------|---------|----------|\n| `eleven_v3` | 70+ | Standard | Highest quality, emotional range |\n| `eleven_multilingual_v2` | 29 | Standard | High quality, long-form content |\n| `eleven_flash_v2_5` | 32 | ~75ms | Ultra-low latency, real-time |\n| `eleven_flash_v2` | English | ~75ms | English-only, fastest |\n| `eleven_turbo_v2_5` | 32 | ~250-300ms | Balanced quality/speed |\n| `eleven_turbo_v2` | English | ~250-300ms | English-only, balanced |\n\n## Voice IDs\n\nUse pre-made voices or create custom voices in the dashboard.\n\n**Popular voices:**\n- `JBFqnCBsd6RMkjVDRZzb` - George (male, narrative)\n- `EXAVITQu4vr4xnSDxMaL` - Sarah (female, soft)\n- `onwK4e9ZLuTAKqWW03F9` - Daniel (male, authoritative)\n- `XB0fDUnXU5powFXDhCwa` - Charlotte (female, conversational)\n\n```python\nvoices = client.voices.get_all()\nfor voice in voices.voices:\n    print(f\"{voice.voice_id}: {voice.name}\")\n```\n\n## Voice Settings\n\nFine-tune how the voice sounds:\n\n- **Stability**: How consistent the voice stays. Lower values = more emotional range and variation, but can sound unstable. Higher = steady, predictable delivery.\n- **Similarity boost**: How closely to match the original voice sample. Higher values sound more like the original but may amplify audio artifacts.\n- **Style**: Exaggerates the voice's unique style characteristics (only works with v2+ models).\n- **Speaker boost**: Post-processing that enhances clarity and voice similarity.\n\n```python\nfrom elevenlabs import VoiceSettings\n\naudio = client.text_to_speech.convert(\n    text=\"Customize my voice settings.\",\n    voice_id=\"JBFqnCBsd6RMkjVDRZzb\",\n    voice_settings=VoiceSettings(\n        stability=0.5,\n        similarity_boost=0.75,\n        style=0.5,\n        speed=1.0,             # 0.25 to 4.0 (default 1.0)\n        use_speaker_boost=True\n    )\n)\n```\n\n## Language Selection\n\nUse `language_code` with models that support language enforcement to guide pronunciation and text normalization. Unsupported language codes are ignored, and `language_code` is not supported on `eleven_multilingual_v2`.\n\n```python\naudio = client.text_to_speech.convert(\n    text=\"Bonjour, comment allez-vous?\",\n    voice_id=\"JBFqnCBsd6RMkjVDRZzb\",\n    model_id=\"eleven_v3\",\n    language_code=\"fr\"  # ISO 639-1 code\n)\n```\n\n## Text Normalization\n\nControls how numbers, dates, and abbreviations are converted to spoken words. For example, \"01/15/2026\" becomes \"January fifteenth, twenty twenty-six\":\n\n- `\"auto\"` (default): Model decides based on context\n- `\"on\"`: Always normalize (use when you want natural speech)\n- `\"off\"`: Speak literally (use when you want \"zero one slash one five...\")\n\n```python\naudio = client.text_to_speech.convert(\n    text=\"Call 1-800-555-0123 on 01/15/2026\",\n    voice_id=\"JBFqnCBsd6RMkjVDRZzb\",\n    apply_text_normalization=\"on\"\n)\n```\n\n## Request Stitching\n\nWhen generating long audio in multiple requests, the audio can have pops, unnatural pauses, or tone shifts at the boundaries. Request stitching solves this by letting each request know what comes before/after it:\n\n```python\n# First request\naudio1 = client.text_to_speech.convert(\n    text=\"This is the first part.\",\n    voice_id=\"JBFqnCBsd6RMkjVDRZzb\",\n    next_text=\"And this continues the story.\"\n)\n\n# Second request using previous context\naudio2 = client.text_to_speech.convert(\n    text=\"And this continues the story.\",\n    voice_id=\"JBFqnCBsd6RMkjVDRZzb\",\n    previous_text=\"This is the first part.\"\n)\n```\n\n## Output Formats\n\n| Format | Description |\n|--------|-------------|\n| `mp3_44100_128` | MP3 44.1kHz 128kbps (default) - compressed, good for web/apps |\n| `mp3_44100_192` | MP3 44.1kHz 192kbps (Creator+) - higher quality compressed |\n| `mp3_44100_64` | MP3 44.1kHz 64kbps - lower quality, smaller files |\n| `mp3_22050_32` | MP3 22.05kHz 32kbps - smallest MP3 files |\n| `pcm_16000` | Raw PCM 16kHz - use for real-time processing |\n| `pcm_22050` | Raw PCM 22.05kHz |\n| `pcm_24000` | Raw PCM 24kHz - good balance for streaming |\n| `pcm_44100` | Raw PCM 44.1kHz (Pro+) - CD quality |\n| `pcm_48000` | Raw PCM 48kHz (Pro+) - highest quality |\n| `ulaw_8000` | μ-law 8kHz - standard for phone systems (Twilio, telephony) |\n| `alaw_8000` | A-law 8kHz - telephony (alternative to μ-law) |\n| `opus_48000_64` | Opus 48kHz 64kbps - efficient streaming codec |\n| `wav_44100` | WAV 44.1kHz - uncompressed with headers |\n\n## Streaming\n\nFor real-time applications, use the `stream` method (returns audio chunks as they're generated):\n\n```python\naudio_stream = client.text_to_speech.stream(\n    text=\"This text will be streamed as audio.\",\n    voice_id=\"JBFqnCBsd6RMkjVDRZzb\",\n    model_id=\"eleven_flash_v2_5\"  # Ultra-low latency\n)\n\nfor chunk in audio_stream:\n    play_audio(chunk)\n```\n\nSee [references/streaming.md](references/streaming.md) for WebSocket streaming.\n\n## Error Handling\n\n```python\ntry:\n    audio = client.text_to_speech.convert(\n        text=\"Generate speech\",\n        voice_id=\"invalid-voice-id\"\n    )\nexcept Exception as e:\n    print(f\"API error: {e}\")\n```\n\nCommon errors:\n- **401**: Invalid API key\n- **422**: Invalid parameters (check voice_id, model_id)\n- **429**: Rate limit exceeded\n\n## Tracking Costs\n\nMonitor character usage via response headers (`x-character-count`, `request-id`):\n\n```python\nresponse = client.text_to_speech.convert.with_raw_response(\n    text=\"Hello!\", voice_id=\"JBFqnCBsd6RMkjVDRZzb\", model_id=\"eleven_multilingual_v2\"\n)\naudio = response.parse()\nprint(f\"Characters used: {response.headers.get('x-character-count')}\")\n```\n\n## References\n\n- [Installation Guide](references/installation.md)\n- [Streaming Audio](references/streaming.md)\n- [Voice Settings](references/voice-settings.md)\n","contentSource":"https://raw.githubusercontent.com/elevenlabs/skills/main/text-to-speech/SKILL.md","contentFetchedAt":"2026-07-28T23:15:41.388Z"},{"name":"speech-to-text","url":"https://github.com/elevenlabs/skills/tree/main/speech-to-text","install":"npx skills add elevenlabs/skills --skill speech-to-text","sdk":"elevenlabs","key":"elevenlabs/speech-to-text","description":"Transcribe audio to text using ElevenLabs Scribe v2. Use when converting audio/video to text, generating subtitles, transcribing meetings, or processing spoken content.","hasContent":true,"content":"---\nname: speech-to-text\ndescription: Transcribe audio to text using ElevenLabs Scribe v2. Use when converting audio/video to text, generating subtitles, transcribing meetings, or processing spoken content.\nlicense: MIT\ncompatibility: Requires internet access and an ElevenLabs API key (ELEVENLABS_API_KEY).\nmetadata: {\"openclaw\": {\"requires\": {\"env\": [\"ELEVENLABS_API_KEY\"]}, \"primaryEnv\": \"ELEVENLABS_API_KEY\"}}\n---\n\n# ElevenLabs Speech-to-Text\n\nTranscribe audio to text with Scribe v2 - supports 90+ languages, speaker diarization, and word-level timestamps.\n\n> **Setup:** See [Installation Guide](references/installation.md). For JavaScript, use `@elevenlabs/*` packages only.\n\n## Quick Start\n\n### Python\n\n```python\nfrom elevenlabs import ElevenLabs\n\nclient = ElevenLabs()\n\nwith open(\"audio.mp3\", \"rb\") as audio_file:\n    result = client.speech_to_text.convert(file=audio_file, model_id=\"scribe_v2\")\n\nprint(result.text)\n```\n\n### JavaScript\n\n```javascript\nimport { ElevenLabsClient } from \"@elevenlabs/elevenlabs-js\";\nimport { createReadStream } from \"fs\";\n\nconst client = new ElevenLabsClient();\nconst result = await client.speechToText.convert({\n  file: createReadStream(\"audio.mp3\"),\n  modelId: \"scribe_v2\",\n});\nconsole.log(result.text);\n```\n\n### cURL\n\n```bash\ncurl -X POST \"https://api.elevenlabs.io/v1/speech-to-text\" \\\n  -H \"xi-api-key: $ELEVENLABS_API_KEY\" -F \"file=@audio.mp3\" -F \"model_id=scribe_v2\"\n```\n\n## Models\n\n| Model ID | Description | Best For |\n|----------|-------------|----------|\n| `scribe_v2` | State-of-the-art accuracy, 90+ languages | Batch transcription, subtitles, long-form audio |\n| `scribe_v2_realtime` | Low latency (~150ms) | Live transcription, voice agents |\n| `scribe_v2_realtime_turbo` | Realtime transcription variant | Live transcription |\n| `scribe_v2_realtime_lite` | Realtime transcription variant | Live transcription |\n\n## Transcription with Timestamps\n\nWord-level timestamps include type classification and speaker identification:\n\n```python\nresult = client.speech_to_text.convert(\n    file=audio_file, model_id=\"scribe_v2\", timestamps_granularity=\"word\"\n)\n\nfor word in result.words:\n    print(f\"{word.text}: {word.start}s - {word.end}s (type: {word.type})\")\n\n```\n\n## Speaker Diarization\n\nIdentify WHO said WHAT - the model labels each word with a speaker ID, useful for meetings, interviews, or any multi-speaker audio:\n\n```python\nresult = client.speech_to_text.convert(\n    file=audio_file,\n    model_id=\"scribe_v2\",\n    diarize=True\n)\n\nfor word in result.words:\n    print(f\"[{word.speaker_id}] {word.text}\")\n```\n\nFor call recordings, the batch API can label diarized speakers as `agent` and `customer` by setting `detect_speaker_roles=true` alongside `diarize=true`. This option is not compatible with `use_multi_channel=true`.\n\nIf your workspace has registered speaker profiles, set `use_speaker_library=true` with `diarize=true` to match detected speakers against the speaker library.\n\n```bash\ncurl -X POST \"https://api.elevenlabs.io/v1/speech-to-text\" \\\n  -H \"xi-api-key: $ELEVENLABS_API_KEY\" \\\n  -F \"file=@call.mp3\" \\\n  -F \"model_id=scribe_v2\" \\\n  -F \"diarize=true\" \\\n  -F \"detect_speaker_roles=true\" \\\n  -F \"use_speaker_library=true\"\n```\n\n## Multichannel Audio\n\nUse `use_multi_channel=true` when each speaker is isolated on a separate audio channel. By default, the API returns one transcript per channel under `transcripts`; set `multichannel_output_style=\"combined\"` to receive one transcript merged by timestamp, with `channel_index` on each word.\n\n```python\nresult = client.speech_to_text.convert(\n    file=audio_file,\n    model_id=\"scribe_v2\",\n    use_multi_channel=True,\n    multichannel_output_style=\"combined\",\n)\n```\n\n## Keyterm Prompting\n\nHelp the model recognize specific words it might otherwise mishear - product names, technical jargon, or unusual spellings (up to 100 terms):\n\n```python\nresult = client.speech_to_text.convert(\n    file=audio_file,\n    model_id=\"scribe_v2\",\n    keyterms=[\"ElevenLabs\", \"Scribe\", \"API\"]\n)\n```\n\n## Language Detection\n\nAutomatic detection with optional language hint:\n\n```python\nresult = client.speech_to_text.convert(\n    file=audio_file,\n    model_id=\"scribe_v2\",\n    language_code=\"eng\"  # ISO 639-1 or ISO 639-3 code\n)\n\nprint(f\"Detected: {result.language_code} ({result.language_probability:.0%})\")\n```\n\n## Supported Formats\n\n**Audio:** MP3, WAV, M4A, FLAC, OGG, WebM, AAC, AIFF, Opus\n**Video:** MP4, AVI, MKV, MOV, WMV, FLV, WebM, MPEG, 3GPP\n\n**Limits:** Up to 5.0GB file size, 10 hours duration\n\n## Response Format\n\n```json\n{\n  \"text\": \"The full transcription text\",\n  \"language_code\": \"eng\",\n  \"language_probability\": 0.98,\n  \"words\": [\n    {\"text\": \"The\", \"start\": 0.0, \"end\": 0.15, \"type\": \"word\", \"speaker_id\": \"speaker_0\"},\n    {\"text\": \" \", \"start\": 0.15, \"end\": 0.16, \"type\": \"spacing\", \"speaker_id\": \"speaker_0\"}\n  ]\n}\n```\n\n**Word types:**\n- `word` - An actual spoken word\n- `spacing` - Whitespace between words (useful for precise timing)\n- `audio_event` - Non-speech sounds the model detected (laughter, applause, music, etc.)\n\n## Error Handling\n\n```python\ntry:\n    result = client.speech_to_text.convert(file=audio_file, model_id=\"scribe_v2\")\nexcept Exception as e:\n    print(f\"Transcription failed: {e}\")\n```\n\nCommon errors:\n- **401**: Invalid API key\n- **422**: Invalid parameters\n- **429**: Rate limit exceeded\n\n## Tracking Costs\n\nMonitor usage via `request-id` response header:\n\n```python\nresponse = client.speech_to_text.convert.with_raw_response(file=audio_file, model_id=\"scribe_v2\")\nresult = response.parse()\nprint(f\"Request ID: {response.headers.get('request-id')}\")\n```\n\n## Real-Time Streaming\n\nFor live transcription with ultra-low latency (~150ms), use the real-time API. The real-time API produces two types of transcripts:\n\n- **Partial transcripts**: Interim results that update frequently as audio is processed - use these for live feedback (e.g., showing text as the user speaks)\n- **Committed transcripts**: Final, stable results after you \"commit\" - use these as the source of truth for your application\n\nA \"commit\" tells the model to finalize the current segment. You can commit manually (e.g., when the user pauses) or use Voice Activity Detection (VAD) to auto-commit on silence.\n\n### Python (Server-Side)\n\n```python\nimport asyncio\nfrom elevenlabs import ElevenLabs\n\nclient = ElevenLabs()\n\nasync def transcribe_realtime():\n    async with client.speech_to_text.realtime.connect(\n        model_id=\"scribe_v2_realtime\",\n        include_timestamps=True,\n        keyterms=[\"ElevenLabs\", \"Scribe\"],\n        no_verbatim=True,\n    ) as connection:\n        await connection.stream_url(\"https://example.com/audio.mp3\")\n\n        async for event in connection:\n            if event.type == \"partial_transcript\":\n                print(f\"Partial: {event.text}\")\n            elif event.type == \"committed_transcript\":\n                print(f\"Final: {event.text}\")\n\nasyncio.run(transcribe_realtime())\n```\n\n### JavaScript (Client-Side with React)\n\n```typescript\nimport { useScribe, CommitStrategy } from \"@elevenlabs/react\";\n\nfunction TranscriptionComponent() {\n  const [transcript, setTranscript] = useState(\"\");\n\n  const scribe = useScribe({\n    modelId: \"scribe_v2_realtime\",\n    commitStrategy: CommitStrategy.VAD, // Auto-commit on silence for mic input\n    keyterms: [\"ElevenLabs\", \"Scribe\"],\n    noVerbatim: true,\n    includeLanguageDetection: true,\n    onPartialTranscript: (data) => console.log(\"Partial:\", data.text),\n    onCommittedTranscript: (data) => setTranscript((prev) => prev + data.text),\n  });\n\n  const start = async () => {\n    // Get token from your backend (never expose API key to client)\n    const { token } = await fetch(\"/scribe-token\").then((r) => r.json());\n\n    await scribe.connect({\n      token,\n      microphone: { echoCancellation: true, noiseSuppression: true },\n    });\n  };\n\n  return <button onClick={start}>Start Recording</button>;\n}\n```\n\n### Commit Strategies\n\n| Strategy | Description |\n|----------|-------------|\n| **Manual** | You call `commit()` when ready - use for file processing or when you control the audio segments |\n| **VAD** | Voice Activity Detection auto-commits when silence is detected - use for live microphone input |\n\nSet `includeLanguageDetection: true` to receive the detected language code on committed transcript\nevents that include timestamps.\n\n```typescript\n// React: set commitStrategy on the hook (recommended for mic input)\nimport { useScribe, CommitStrategy } from \"@elevenlabs/react\";\n\nconst scribe = useScribe({\n  modelId: \"scribe_v2_realtime\",\n  commitStrategy: CommitStrategy.VAD,\n  keyterms: [\"ElevenLabs\", \"Scribe\"],\n  noVerbatim: true,\n  // Optional VAD tuning:\n  vadSilenceThresholdSecs: 1.5,\n  vadThreshold: 0.4,\n});\n```\n\n```javascript\n// JavaScript client: pass vad config on connect\nconst connection = await client.speechToText.realtime.connect({\n  modelId: \"scribe_v2_realtime\",\n  keyterms: [\"ElevenLabs\", \"Scribe\"],\n  noVerbatim: true,\n  vad: {\n    silenceThresholdSecs: 1.5,\n    threshold: 0.4,\n  },\n});\n```\n\n### Event Types\n\n| Event | Description |\n|-------|-------------|\n| `partial_transcript` | Live interim results |\n| `committed_transcript` | Final results after commit |\n| `committed_transcript_with_timestamps` | Final with word timing |\n| `error` | Error occurred |\n\nSee real-time references for complete documentation.\n\n## References\n\n- [Installation Guide](references/installation.md)\n- [Transcription Options](references/transcription-options.md)\n- [Real-Time Client-Side Streaming](references/realtime-client-side.md)\n- [Real-Time Server-Side Streaming](references/realtime-server-side.md)\n- [Commit Strategies](references/realtime-commit-strategies.md)\n- [Real-Time Event Reference](references/realtime-events.md)\n","contentSource":"https://raw.githubusercontent.com/elevenlabs/skills/main/speech-to-text/SKILL.md","contentFetchedAt":"2026-07-28T23:15:41.498Z"},{"name":"agents","url":"https://github.com/elevenlabs/skills/tree/main/agents","install":"npx skills add elevenlabs/skills --skill agents","sdk":"elevenlabs","key":"elevenlabs/agents","description":"Build voice AI agents with ElevenLabs. Use when creating voice assistants, customer service bots, interactive voice characters, or any real-time voice conversation experience.","hasContent":true,"content":"---\nname: agents\ndescription: Build voice AI agents with ElevenLabs. Use when creating voice assistants, customer service bots, interactive voice characters, or any real-time voice conversation experience.\nlicense: MIT\ncompatibility: Requires internet access and an ElevenLabs API key (ELEVENLABS_API_KEY).\nmetadata: {\"openclaw\": {\"requires\": {\"env\": [\"ELEVENLABS_API_KEY\"]}, \"primaryEnv\": \"ELEVENLABS_API_KEY\"}}\n---\n\n# ElevenLabs Agents Platform\n\nBuild voice AI agents with natural conversations, multiple LLM providers, custom tools, and easy web embedding.\n\n> **Setup:** See [Installation Guide](references/installation.md) for CLI and SDK setup.\n\n## Quick Start with CLI\n\nThe ElevenLabs CLI is the recommended way to create and manage agents:\n\n```bash\n# Install CLI and authenticate\nnpm install -g @elevenlabs/cli\nelevenlabs auth login\n\n# Initialize project and create an agent\nelevenlabs agents init\nelevenlabs agents add \"My Assistant\" --template complete\n\n# Push to ElevenLabs platform\nelevenlabs agents push\n```\n\n**Available templates:** `complete`, `minimal`, `voice-only`, `text-only`, `customer-service`, `assistant`\n\n### Python\n\n```python\nfrom elevenlabs import ElevenLabs\n\nclient = ElevenLabs()\n\nagent = client.conversational_ai.agents.create(\n    name=\"My Assistant\",\n    conversation_config={\n        \"agent\": {\n            \"first_message\": \"Hello! How can I help?\",\n            \"language\": \"en\",\n            \"prompt\": {\n                \"prompt\": \"You are a helpful assistant. Be concise and friendly.\",\n                \"llm\": \"gemini-2.0-flash\",\n                \"temperature\": 0.7\n            }\n        },\n        \"tts\": {\"voice_id\": \"JBFqnCBsd6RMkjVDRZzb\"}\n    }\n)\n```\n\n### JavaScript\n\n```javascript\nimport { ElevenLabsClient } from \"@elevenlabs/elevenlabs-js\";\nconst client = new ElevenLabsClient();\n\nconst agent = await client.conversationalAi.agents.create({\n  name: \"My Assistant\",\n  conversationConfig: {\n    agent: {\n      firstMessage: \"Hello! How can I help?\",\n      language: \"en\",\n      prompt: {\n        prompt: \"You are a helpful assistant.\",\n        llm: \"gemini-2.0-flash\",\n        temperature: 0.7\n      }\n    },\n    tts: { voiceId: \"JBFqnCBsd6RMkjVDRZzb\" }\n  }\n});\n```\n\n### cURL\n\n```bash\ncurl -X POST \"https://api.elevenlabs.io/v1/convai/agents/create\" \\\n  -H \"xi-api-key: $ELEVENLABS_API_KEY\" -H \"Content-Type: application/json\" \\\n  -d '{\"name\": \"My Assistant\", \"conversation_config\": {\"agent\": {\"first_message\": \"Hello!\", \"language\": \"en\", \"prompt\": {\"prompt\": \"You are helpful.\", \"llm\": \"gemini-2.0-flash\"}}, \"tts\": {\"voice_id\": \"JBFqnCBsd6RMkjVDRZzb\"}}}'\n```\n\n## Starting Conversations\n\n### Temporary LiveKit WebSocket Pin\n\nUntil the ElevenLabs LiveKit server supports `/rtc/v1`, browser clients using WebRTC can fail or stall during the underlying LiveKit WebSocket handshake with `livekit-client` versions newer than `2.16.1`. For React, Next.js, Electron, or other `@elevenlabs/client` / `@elevenlabs/react` integrations that use `connectionType: \"webrtc\"` or hit `wss://livekit.rtc.elevenlabs.io/rtc/v1`, add this temporary pin to `package.json`:\n\n```json\n{\n  \"overrides\": {\n    \"livekit-client\": \"2.16.1\"\n  }\n}\n```\n\nUse the pin when the app logs `/rtc/v1` 404s, `v1 RTC path not found`, or `could not establish pc connection` during session startup. This is a LiveKit server compatibility workaround for WebRTC sessions, not the ElevenLabs `connectionType: \"websocket\"` transport. Remove it after the upstream LiveKit server or SDK issue is fixed.\n\n**Server-side (Python):** Get signed URL for client connection:\n```python\nsigned_url = client.conversational_ai.conversations.get_signed_url(\n    agent_id=\"your-agent-id\",\n    environment=\"staging\",\n)\n```\n\n**Client-side (JavaScript):**\n```javascript\nimport { Conversation } from \"@elevenlabs/client\";\n\nconst conversation = await Conversation.startSession({\n  agentId: \"your-agent-id\",\n  environment: \"staging\",\n  overrides: { asr: { keywords: [\"ElevenLabs\", \"TechCorp\"] } },\n  onMessage: (msg) => console.log(\"Agent:\", msg.message),\n  onUserTranscript: (t) => console.log(\"User:\", t.message),\n  onPing: (event) => console.log(\"Estimated latency:\", event.ping_ms),\n  onError: (e) => console.error(e)\n});\n```\n\n**React Hook:** Wrap hook consumers in `ConversationProvider`. Prefer granular hooks such as\n`useConversationControls` and `useConversationStatus` for session controls and UI state;\n`useConversation` remains available as the convenience all-in-one hook. Pass provider-level\ncallbacks such as `onError` when you want React to handle conversation errors in one place.\n```typescript\nimport {\n  ConversationProvider,\n  useConversationControls,\n  useConversationStatus,\n} from \"@elevenlabs/react\";\n\nfunction Agent({ signedUrl }: { signedUrl: string }) {\n  const { startSession, endSession } = useConversationControls();\n  const { status } = useConversationStatus();\n\n  if (status === \"connected\") {\n    return <button onClick={endSession}>End conversation</button>;\n  }\n\n  return (\n    <button onClick={() => startSession({ signedUrl })}>\n      Start conversation\n    </button>\n  );\n}\n\nfunction App({ signedUrl }: { signedUrl: string }) {\n  return (\n    <ConversationProvider\n      onError={(error) => console.error(\"Conversation error:\", error)}\n      onPing={(event) => console.log(\"Estimated latency:\", event.ping_ms)}\n    >\n      <Agent signedUrl={signedUrl} />\n    </ConversationProvider>\n  );\n}\n```\n\n## Configuration\n\n| Provider | Models |\n|----------|--------|\n| OpenAI | `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, `gpt-5.5`, `gpt-5.5-2026-04-23`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.4-nano`, `gpt-5.4-2026-03-05`, `gpt-5.4-mini-2026-03-17`, `gpt-5.4-nano-2026-03-17`, `gpt-5`, `gpt-5-mini`, `gpt-5-nano`, `gpt-4.1`, `gpt-4.1-mini`, `gpt-4.1-nano`, `gpt-4o`, `gpt-4o-mini`, `gpt-4-turbo` |\n| Anthropic | `claude-opus-4-7`, `claude-sonnet-4-6`, `claude-sonnet-4-5`, `claude-sonnet-4`, `claude-haiku-4-5`, `claude-3-7-sonnet`, `claude-3-5-sonnet`, `claude-3-haiku` |\n| Google | `gemini-3.1-flash-lite-preview`, `gemini-3.1-pro-preview`, `gemini-3-pro-preview`, `gemini-3-flash-preview`, `gemini-2.5-flash`, `gemini-2.5-flash-lite`, `gemini-2.0-flash`, `gemini-2.0-flash-lite` |\n| ElevenLabs | `glm-45-air-fp8`, `qwen3-30b-a3b`, `qwen36-35b-a3b`, `qwen35-35b-a3b`, `qwen35-397b-a17b`, `gpt-oss-120b` |\n| Custom | `custom-llm` (bring your own endpoint) |\n\nUse `GET /v1/convai/llm/list` to inspect the current model catalog, including deprecation state, token/context limits, capability flags such as image-input support, and model-specific reasoning effort support.\n\n**Popular voices:** `JBFqnCBsd6RMkjVDRZzb` (George), `EXAVITQu4vr4xnSDxMaL` (Sarah), `onwK4e9ZLuTAKqWW03F9` (Daniel), `XB0fDUnXU5powFXDhCwa` (Charlotte)\n\n**Turn eagerness:** `patient` (waits longer for user to finish), `normal`, or `eager` (responds quickly)\n\nSee [Agent Configuration](references/agent-configuration.md) for all options.\n\n## System Prompt Structure\n\nSection the prompt with markdown headings — the model prioritizes and interprets instructions more reliably ([prompting guide](https://elevenlabs.io/docs/eleven-agents/best-practices/prompting-guide)):\n\n```\n# Personality   – named character, 2-3 traits\n# Environment   – where they work, who they talk to\n# Tone          – vocal style as 4-5 bullets\n# Goal          – what success looks like (numbered for multi-step flows)\n```\n\nKeep instructions short and action-based. Mark critical steps with \"This step is important.\" For critical refusal/safety rules, include concise instructions in the prompt and also configure independent custom Guardrails via `platform_settings.guardrails` (see [Guardrails](#guardrails)).\n\n## Tools\n\nExtend agents with webhook, client, or built-in system tools. Tools are defined inside `conversation_config.agent.prompt`:\n\nWorkspace environment variables can resolve per-environment server tool URLs, headers, and auth connections, and runtime system variables such as `{{system__conversation_history}}` can pass full conversation context into tool calls when needed.\n\n```python\n\"prompt\": {\n    \"prompt\": \"You are a helpful assistant that can check the weather.\",\n    \"llm\": \"gemini-2.0-flash\",\n    \"tools\": [\n        # Webhook: server-side API call\n        {\"type\": \"webhook\", \"name\": \"get_weather\", \"description\": \"Get weather\",\n         \"api_schema\": {\"url\": \"https://api.example.com/weather\", \"method\": \"POST\",\n             \"request_body_schema\": {\"type\": \"object\", \"properties\": {\"location\": {\"type\": \"string\"}}, \"required\": [\"location\"]}}},\n        # Client: runs in the browser\n        {\"type\": \"client\", \"name\": \"show_product\", \"description\": \"Display a product\",\n         \"parameters\": {\"type\": \"object\", \"properties\": {\"productId\": {\"type\": \"string\"}}, \"required\": [\"productId\"]}}\n    ],\n    \"built_in_tools\": {\n        \"end_call\": {},\n        \"transfer_to_number\": {\"transfers\": [{\"transfer_destination\": {\"type\": \"phone\", \"phone_number\": \"+1234567890\"}, \"condition\": \"User asks for human support\"}]},\n        \"start_procedure\": {}\n    }\n}\n```\n\n**Client tools** run in browser:\n```javascript\nclientTools: {\n  show_product: async ({ productId }) => {\n    document.getElementById(\"product\").src = `/products/${productId}`;\n    return { success: true };\n  }\n}\n```\n\nSee [Client Tools Reference](references/client-tools.md) for complete documentation.\n\n### Built-in System Tools\n\nSet under `conversation_config.agent.prompt.built_in_tools`. `{}` enables defaults; provide `description` to customize; omit to disable.\n\n| Tool | Enable for |\n|------|------------|\n| `end_call` | All agents |\n| `language_detection` | Multilingual agents |\n| `transfer_to_number` | Phone-based human escalation |\n| `transfer_to_agent` | Multi-agent workflows |\n| `start_procedure` | Procedure-guided conversations |\n| `end_procedure` | Completing active procedures |\n| `skip_turn` | Tutoring / coaching (silent listening) |\n| `voicemail_detection` | Outbound calling |\n| `play_keypad_touch_tone` | IVR navigation |\n\n`run_subagent` is a system tool for delegating a task to another configured agent. Add it to\n`conversation_config.agent.prompt.tools` with `params.system_tool_type: \"run_subagent\"` and an\n`agents` array. Each entry requires `agent_id` and `description`; `branch_id` and a JSON-schema\n`parameters` object are optional.\n\n### Integration Tools\n\nPre-built connectors managed by the platform. Create a connection with credentials, then attach via `tool_ids`:\n\n| Integration | Use case |\n|-------------|----------|\n| `calcom` | Scheduling appointments |\n| `salesforce` | CRM lookups, case creation |\n| `hubspot` | CRM, marketing, contacts |\n| `zendesk` | Support ticketing |\n\nThree-step flow: `POST /v1/convai/api-integrations/{id}/connections` → `GET /v1/convai/api-integrations/{id}/tools` → `POST /v1/convai/tools` with `api_integration_id` and `api_integration_connection_id`. Attach to the agent with `\"prompt\": {\"tool_ids\": [\"tool_xxxx\"]}`. Inline `tools` and `tool_ids` can coexist — prefer an integration over a duplicate custom webhook.\n\n### Public-API Webhook Examples\n\nNo-auth APIs useful for prototypes (URLs must be HTTPS):\n\n| Tool | URL | Purpose |\n|------|-----|---------|\n| `get_weather` | `https://wttr.in/{location}?format=j1` | Current weather |\n| `search_wikipedia` | `https://en.wikipedia.org/api/rest_v1/page/summary/{topic}` | Topic summary |\n| `get_exchange_rate` | `https://open.er-api.com/v6/latest/{base_currency}` | FX rates |\n\n## Workflows\n\nRoute conversations through discrete steps with branching logic. Define under the agent's top-level `workflow` field. Reference: [Agent Workflows](https://elevenlabs.io/docs/eleven-agents/customization/agent-workflows).\n\n**Node types:** `start` (ID must be `\"start_node\"`), `end`, `override_agent` (subagent step with `label` + `additional_prompt`), `dispatch_tool` (executes a tool with success/failure routing), `agent_transfer`, `transfer_to_number`.\n\n**Edge types:** `unconditional`, `llm` (natural-language condition), `expression` (deterministic data check). Tool nodes have separate success/failure edges.\n\n**Scope tools per step** with `additional_tool_ids` on a node — prevents the wrong tool firing at the wrong step. Set `additional_tool_ids: []` on conversational routing nodes such as greeting and `classify_intent` so they only converse:\n\n```json\n{\n  \"type\": \"override_agent\",\n  \"label\": \"Book Appointment\",\n  \"additional_prompt\": \"Discuss preferred dates and doctors. Show the booking form once agreed.\",\n  \"entry_behavior\": \"wait_for_user\",\n  \"additional_tool_ids\": [\"show_booking_form\", \"display_appointment_card\"],\n  \"position\": {\"x\": 0, \"y\": 400}\n}\n```\n\nInclude `position` (`{x, y}`) on every node so the editor renders cleanly. Start at `y=0`, put `end` at the bottom, and space branches horizontally at `x=-150` and `x=150`; suggested spacing is 200px vertical between levels and 300px horizontal between branches. Keep workflows to 4-7 nodes and always have a path to `end`.\n\nUse `entry_behavior` on `override_agent` nodes to choose whether a sub-agent speaks immediately (`generate_immediately`), waits for user input (`wait_for_user`), or lets the platform decide (`auto`).\n\nFor nested agent transfers, set `enable_nesting` on a `standalone_agent` node and\n`return_when_nested` on an `end` node that should return control to the parent workflow.\n\n## Guardrails\n\nLayered safety enforcement that runs independently of the LLM — configured under `platform_settings.guardrails`, not in the system prompt. Reference: [Guardrails](https://elevenlabs.io/docs/eleven-agents/best-practices/guardrails).\n\n```json\n\"platform_settings\": {\n  \"guardrails\": {\n    \"version\": \"1\",\n    \"focus\": {\"is_enabled\": true},\n    \"prompt_injection\": {\"is_enabled\": true},\n    \"content\": {\"config\": {\"harassment\": {\"is_enabled\": true, \"threshold\": 0.5}}},\n    \"custom\": {\n      \"config\": {\n        \"configs\": [{\n          \"is_enabled\": true,\n          \"name\": \"No medical diagnoses\",\n          \"prompt\": \"Block the agent from providing medical diagnoses or treatment advice.\",\n          \"execution_mode\": \"blocking\",\n          \"model\": \"gemini-2.5-flash-lite\",\n          \"history_message_count\": 1,\n          \"trigger_action\": {\"type\": \"retry\", \"feedback\": \"Reason: {{trigger_reason}}\"}\n        }]\n      }\n    }\n  }\n}\n```\n\n**Types:** `focus` (on-topic), `prompt_injection` (manipulation defense), `content` (category filters), `custom` (LLM-evaluated domain rules). Content categories include `harassment`, `profanity`, `sexual`, `violence`, `self_harm`, and `medical_and_legal_information` — threshold range `0.0`–`1.0` (default `0.3`). Custom rules use `execution_mode: \"blocking\"` with a `model`, `history_message_count`, and `trigger_action` (e.g., `retry` with feedback). Custom guardrails evaluate in parallel and fail-open.\n\n**Per vertical:** healthcare/finance/legal → enable `medical_and_legal_information`; education/youth → `sexual`/`violence`/`self_harm`/`profanity`; support/sales → `harassment`/`profanity`. All agents benefit from `focus` + `prompt_injection` + 2-4 custom rules.\n\n## Testing Agents\n\nThree test types via `POST /v1/convai/agent-testing/create`, then attached with PATCH on the agent. Reference: [Agent Testing](https://elevenlabs.io/docs/eleven-agents/customization/agent-testing).\n\n| Type | Purpose |\n|------|---------|\n| `llm` | Scenario test — does the agent respond appropriately to a message? |\n| `tool` | Tool-call test — right tool, right parameters? |\n| `simulation` | Multi-turn flow with a simulated user persona |\n\n```json\n// Tool-call test (snake_case throughout; chat_history role is \"user\" or \"agent\")\n{\n  \"name\": \"Books with correct doctor and date\",\n  \"type\": \"tool\",\n  \"chat_history\": [\n    {\"role\": \"user\", \"message\": \"Dr. Smith on March 5 at 2pm\", \"time_in_call_secs\": 10}\n  ],\n  \"tool_call_parameters\": {\n    \"referenced_tool\": {\"id\": \"show_booking_form\", \"type\": \"client\"},\n    \"parameters\": [\n      {\"path\": \"doctor_name\", \"eval\": {\"type\": \"llm\", \"description\": \"Should reference Dr. Smith\"}},\n      {\"path\": \"date\", \"eval\": {\"type\": \"regex\", \"pattern\": \"2025-03-05|March 5\"}}\n    ]\n  }\n}\n```\n\nEval strategies: `exact`, `regex`, `llm`. Prompt evaluation criteria can use binary scoring or\nnumeric scoring with `scoring_mode: \"numeric_uniform\"`, `max_score`, and `score_instructions`;\nnumeric scores are normalized into the aggregate conversation success percentage. Attach via PATCH:\n\n```bash\ncurl -s -X PATCH \"https://api.elevenlabs.io/v1/convai/agents/{agent_id}\" \\\n  -H \"xi-api-key: $ELEVENLABS_API_KEY\" -H \"Content-Type: application/json\" \\\n  -d '{\"platform_settings\": {\"testing\": {\"attached_tests\": [{\"test_id\": \"test_xxxx\"}]}}}'\n```\n\nRun selected tests with `POST /v1/convai/agents/{agent_id}/run-tests`. The request\nbody requires `tests` and accepts `repeat_count` from `1` to `50` for repeated runs.\nSimulation tests can define up to 30 `success_conditions` prompts; all criteria are\nevaluated and merged into the final result.\nFor completed conversations, rerun one evaluation criterion with `POST /v1/convai/conversations/{conversation_id}/analysis/evaluations/run` and a request body containing `evaluation_id`.\n\n## Widget Embedding\n\n```html\n<elevenlabs-convai agent-id=\"your-agent-id\"></elevenlabs-convai>\n<script src=\"https://unpkg.com/@elevenlabs/convai-widget-embed\" async type=\"text/javascript\"></script>\n```\n\nCustomize with attributes: `avatar-image-url`, `action-text`, `start-call-text`, `end-call-text`.\n\nSee [Widget Embedding Reference](references/widget-embedding.md) for all options.\n\n## Outbound Calls\n\nMake outbound phone calls using your agent via Twilio or Exotel integration:\n\nThe examples below use Twilio. See the reference for Exotel REST usage.\n\n### Python\n\n```python\nresponse = client.conversational_ai.twilio.outbound_call(\n    agent_id=\"your-agent-id\",\n    agent_phone_number_id=\"your-phone-number-id\",\n    to_number=\"+1234567890\",\n    call_recording_enabled=True\n)\nprint(f\"Call initiated: {response.conversation_id}\")\n```\n\n### JavaScript\n\n```javascript\nconst response = await client.conversationalAi.twilio.outboundCall({\n  agentId: \"your-agent-id\",\n  agentPhoneNumberId: \"your-phone-number-id\",\n  toNumber: \"+1234567890\",\n  callRecordingEnabled: true,\n});\n```\n\n### cURL\n\n```bash\ncurl -X POST \"https://api.elevenlabs.io/v1/convai/twilio/outbound-call\" \\\n  -H \"xi-api-key: $ELEVENLABS_API_KEY\" -H \"Content-Type: application/json\" \\\n  -d '{\"agent_id\": \"your-agent-id\", \"agent_phone_number_id\": \"your-phone-number-id\", \"to_number\": \"+1234567890\", \"call_recording_enabled\": true}'\n```\n\nSee [Outbound Calls Reference](references/outbound-calls.md) for provider-specific endpoints, configuration overrides, and dynamic variables.\n\n## Managing Agents\n\n### Using CLI (Recommended)\n\n```bash\n# List agents and check status\nelevenlabs agents list\nelevenlabs agents status\n\n# Import agents from platform to local config\nelevenlabs agents pull                      # Import all agents\nelevenlabs agents pull --agent <agent-id>   # Import specific agent\n\n# Push local changes to platform\nelevenlabs agents push              # Upload configurations\nelevenlabs agents push --dry-run    # Preview changes first\n\n# Add tools\nelevenlabs tools add-webhook \"Weather API\"\nelevenlabs tools add-client \"UI Tool\"\n```\n\n### Project Structure\n\nThe CLI creates a project structure for managing agents:\n\n```\nyour_project/\n├── agents.json       # Agent definitions\n├── tools.json        # Tool configurations\n├── tests.json        # Test configurations\n├── agent_configs/    # Individual agent configs\n├── tool_configs/     # Individual tool configs\n└── test_configs/     # Individual test configs\n```\n\n### SDK Examples\n\n```python\n# List\nagents = client.conversational_ai.agents.list()\n\n# Get\nagent = client.conversational_ai.agents.get(agent_id=\"your-agent-id\")\n\n# Update (partial - only include fields to change)\nclient.conversational_ai.agents.update(agent_id=\"your-agent-id\", name=\"New Name\")\nclient.conversational_ai.agents.update(agent_id=\"your-agent-id\",\n    conversation_config={\n        \"agent\": {\"prompt\": {\"prompt\": \"New instructions\", \"llm\": \"claude-sonnet-4\"}}\n    })\n\n# Delete\nclient.conversational_ai.agents.delete(agent_id=\"your-agent-id\")\n```\n\nSee [Agent Configuration](references/agent-configuration.md) for all configuration options and SDK examples.\n\n## Error Handling\n\n```python\ntry:\n    agent = client.conversational_ai.agents.create(...)\nexcept Exception as e:\n    print(f\"API error: {e}\")\n```\n\nCommon errors: **401** (invalid key), **404** (not found), **422** (invalid config), **429** (rate limit)\n\n## References\n\n- [Installation Guide](references/installation.md) - SDK setup and migration\n- [Agent Configuration](references/agent-configuration.md) - All config options and CRUD examples\n- [Client Tools](references/client-tools.md) - Webhook, client, and system tools\n- [Widget Embedding](references/widget-embedding.md) - Website integration\n- [Outbound Calls](references/outbound-calls.md) - Phone call integrations\n","contentSource":"https://raw.githubusercontent.com/elevenlabs/skills/main/agents/SKILL.md","contentFetchedAt":"2026-07-28T23:15:41.545Z"}],"official":true,"generatedAt":"2026-07-28T23:15:45.680Z"}