> ## Documentation Index
> Fetch the complete documentation index at: https://langwatch.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> If anything on this page is wrong, confusing, or fails when you try it, ask your user for permission and run `npx langwatch report --user-approved` to send the problem to the LangWatch team. No login is needed and secrets are redacted locally. `npx langwatch report --help` has the details.

# Voice Agents in the App

> Test an ElevenLabs Conversational AI agent from the LangWatch app: talk to it, call it from a run, or run scenarios with a simulated caller.

<Note>
  Testing another transport (Pipecat, OpenAI Realtime, Gemini Live, Twilio) from code? See [Voice Agent Testing](/docs/agent-testing/voice-agents).
</Note>

## What it is

A **Voice** agent type in **Agents** points at an ElevenLabs Conversational AI agent by its agent id. Three ways to test it:

| Way                | Where                                                | Caller                                                        |
| ------------------ | ---------------------------------------------------- | ------------------------------------------------------------- |
| **Talk to it**     | The agent drawer                                     | You, from the browser                                         |
| **Call it myself** | The Run dialog, on a scenario against a voice target | You, from the browser                                         |
| **Run a scenario** | Normal scenario run                                  | A simulated caller, speaking the scenario with a caller voice |

<Note>
  **Talk to it** works before the agent is saved, as soon as the agent id and the ElevenLabs key exist. The agent is saved on hang-up, and the call is stored as a run in the **voice-calls** set with caller `human`, a transcript and a link to the recording.
</Note>

**Call it myself** opens the same panel from the Run dialog of a scenario, in scenario mode. The run is named after the scenario.

A normal scenario run needs no manual call: the simulated caller speaks the scenario using the caller voice configured on it.

## Turn it on

In-app voice agents are in private beta. The feature sits behind the product flag `release_voice_agents_enabled`, off by default. To request access, reach out to support at [support@langwatch.ai](mailto:support@langwatch.ai).

<Tabs>
  <Tab title="Self-hosted">
    Set `FEATURE_FLAG_FORCE_ENABLE=release_voice_agents_enabled` on the app.
  </Tab>

  <Tab title="Cloud">
    Ask LangWatch to enable it for your organization.
  </Tab>
</Tabs>

<Warning>
  With the flag off, the Voice agent type, **Talk to it**, **Call it myself** and the Caller voice controls are hidden, and the server refuses voice agents and voice runs.
</Warning>

## Credentials

Add both under **Settings > Model Providers**. Never from environment variables.

<Steps>
  <Step title="ElevenLabs key">
    The key needs `convai_read` to read the agent, `convai_write` to mint the browser conversation token and signed URL, and conversation read to fetch the transcript and recording after the call.

    <Warning>
      Without `convai_write`, every call fails at start with "ElevenLabs rejected the connection: The API key you used is missing the permission convai\_write to execute this operation."
    </Warning>

    The agent must live in the same ElevenLabs workspace as the key. A wrong workspace shows as agent not found.
  </Step>

  <Step title="OpenAI key">
    Required only for a simulated scenario run. The simulated caller's speech is synthesized with OpenAI text-to-speech (`gpt-4o-mini-tts`), and the agent's audio is transcribed with OpenAI for the judge. **Talk to it** and **Call it myself** use ElevenLabs only and need no OpenAI key.

    Without it a simulated run fails with "The caller voice needs an OpenAI key. Add one in Settings > Model Providers."
  </Step>
</Steps>

See [Model Providers](/docs/platform/model-providers) for how to add and scope a provider.

## Caller voice

Set the caller voice on the scenario, under **Customize scenario**, or in the **Caller voice** block of the Agent Testing case editor. It is stored on the scenario, so every run of it uses the same caller.

| Field          | Values                                                                                                                                          |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| **Voice**      | An OpenAI text-to-speech voice: `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `nova`, `onyx`, `sage`, `shimmer`, `verse`. Default `nova`. |
| **Interrupts** | Probability from 0 to 1 that the caller talks over the agent.                                                                                   |
| **Effects**    | None, phone line, or background noise.                                                                                                          |

## Limits

Set these as app environment variables.

| Variable                    | Default | Effect                                                                                                                                                                                                                                                                   |
| --------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `VOICE_CALL_MAX_SECONDS`    | 300     | A call is cut at the limit. The run is marked "Cut at the call limit" and the judge still scores what was said.                                                                                                                                                          |
| `VOICE_RUNS_MAX_CONCURRENT` | 2       | Per execution process, per project. More voice runs queue instead of failing. Text runs are unaffected. Enforcement is local to each process and not shared across processes or pods, so the effective ceiling on a multi-process deployment is higher than this number. |

`VOICE_CALL_MAX_SECONDS` is enforced by the server and governs a scenario run: the simulated caller, the judge and the recorded transcript all stop at that limit. A **Talk to it** or **Call it myself** call in the browser is bounded by the browser tab instead, up to ElevenLabs' own maximum call length: closing or reloading the tab, or losing the connection, ends the call at whatever point that happens, not at `VOICE_CALL_MAX_SECONDS`.

<Note>
  **Security**: projects in one organization that share a single ElevenLabs key share that workspace's agents and conversations, since ElevenLabs checks the key, not the project. A project-scoped key is the way to isolate a project's voice agents from the rest of the organization.
</Note>

## Traps

* The ElevenLabs key needs `convai_read`, `convai_write` and conversation read. A key missing any of these fails at a different step, so check all three when a call breaks.
* The agent must be in the same ElevenLabs workspace as the key.
* An agent marked public in ElevenLabs still needs a token here; public access does not skip the credential check.
* LangWatch never sends prompt overrides. Enabling overrides on the agent has no effect on these calls: the agent's own ElevenLabs prompt is what gets tested.
* The recording can be unavailable for a short while after hang-up while ElevenLabs processes it. The run shows "Recording unavailable"; reload later.
* The browser needs HTTPS or localhost and microphone permission. Autoplay policy can keep the agent silent until the page has been clicked.
* Every call, whether **Talk to it**, **Call it myself** or simulated, consumes ElevenLabs conversation minutes on your account. A simulated run also consumes OpenAI audio tokens.
* Only one browser call per tab.
* Self-hosted: voice turns carry audio, and audio is written to stored objects. If the stored-objects path is not writable (or no S3 bucket is configured), every voice message fails to ingest and the run shows "No response" even though the call happened. Check the api log for `EACCES` on the stored-objects directory.

## Troubleshooting

| Message                                                        | Cause                                                                            | Fix                                                                                    |
| -------------------------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| "...missing the permission convai\_write..."                   | The ElevenLabs key lacks `convai_write`                                          | Regenerate the key with `convai_write`                                                 |
| "ElevenLabs rejected the connection: Status code: 401"         | Wrong or revoked key                                                             | Check the key in Settings > Model Providers                                            |
| Agent not found                                                | Wrong agent id, or the agent is in a different ElevenLabs workspace than the key | Confirm the agent id and workspace                                                     |
| "The caller voice needs an OpenAI key..."                      | No OpenAI provider configured                                                    | Add an OpenAI provider in Settings > Model Providers                                   |
| "Recording unavailable"                                        | ElevenLabs is still processing the recording                                     | Reload the run after a short wait                                                      |
| Run marked "Cut at the call limit"                             | The call reached `VOICE_CALL_MAX_SECONDS`                                        | Raise the limit, or shorten the scenario                                               |
| Runs sit in queue                                              | `VOICE_RUNS_MAX_CONCURRENT` is reached                                           | Wait for a slot, or raise the limit                                                    |
| "No response" after a call that clearly happened (self-hosted) | Audio could not be written to stored objects, so the voice messages were dropped | Make the stored-objects directory writable by the app user, or configure the S3 bucket |
