{"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"}