> This is a page from the ElevenLabs documentation. For an index of the documentation, fetch https://elevenlabs.io/docs/llms.txt.

# Voximplant

> **Warning**
>
> Voximplant 통합은 제한된 가용성으로 제공되며 워크스페이스별로 활성화됩니다. 고객 트래픽을 라우팅하기 전에
> ElevenLabs 담당자에게 문의하세요.

## 개요

Voximplant 통합은 Voximplant Media Streams를 사용하여 VoxEngine 시나리오의 통화를 WebSocket을 통해
ElevenAgents의 에이전트에 직접 연결합니다. 전화 기능은 Voximplant에서 담당합니다.
Voximplant이 전화를 걸거나 받고 이후 동작을 결정합니다. 대화는 ElevenAgents에서 담당합니다.
SIP 트렁크나 미들웨어는 필요하지 않습니다.

수신 및 발신 통화는 동일하게 작동합니다. 통화가 연결되면 시나리오는 통화마다 ElevenLabs에 대한 WebSocket을 하나씩 엽니다.
대화가 끝나면 ElevenLabs는 발신자를 상담원에게 연결할지, 전화를 끊을지, 통화를 타임아웃으로 처리할지 알려주는
단일 결정 이벤트를 시나리오에 전송합니다.

## 통합 작동 방식

1. VoxEngine 시나리오가 수신 전화를 받거나 발신 전화를 겁니다.
2. 시나리오가 bearer token으로 ElevenLabs API 키를 사용해 인증하며 에이전트용 ElevenLabs 엔드포인트에
   WebSocket을 엽니다.
3. 시나리오가 모든 `customParameters`를 전달하여 `call.sendMediaTo()`로 미디어 스트림을 시작합니다.
   ElevenLabs는 각 파라미터를 동적 변수로 변환하고 자체 `start`로 응답합니다.
4. Voximplant와 ElevenLabs는 `media` 메시지에서 8 kHz, 16비트 리니어 PCM 오디오를 교환합니다.
5. 대화가 끝나면 ElevenLabs는 에이전트의 마지막 문구 중 나머지 부분, `elevenlabs-decision` 사용자 지정 이벤트 하나,
   그리고 `stop` 메시지를 전송합니다. 그러면 시나리오가 WebSocket을 닫고 결정에 따라 동작합니다.

## 요구 사항

시작하기 전에 다음을 준비하세요.

1. 애플리케이션, 전화번호 또는 발신 기능, 그리고 시나리오 편집 권한이 있는 Voximplant 계정
2. Voximplant 통합이 활성화된 ElevenLabs 워크스페이스
3. ElevenLabs 에이전트 및 전용 API 키

## ElevenLabs 구성

#### 에이전트 생성 또는 선택

ElevenAgents에서 에이전트를 생성하세요. 통화가 연결되는 즉시 말해야 한다면 첫 메시지를 지정하세요.
오디오 형식은 통합에서 설정하므로 에이전트에 구성된 형식은 Voximplant 통화에 사용되지 않습니다.

#### 통화 종료 활성화

**에이전트 → 도구 → 시스템 도구**에서 **통화 종료**를 활성화하여 에이전트가 대화를 끝낼 수 있게 하세요.
에이전트가 통화를 종료할 때 제공하는 사유는 결정과 함께 시나리오로 전송됩니다.

#### Voximplant 전환 규칙 추가(선택 사항)

에이전트가 발신자를 상담원에게 연결할 수 있게 하려면, provider 구성이 `voximplant`인 전환 규칙과 함께
**번호로 전환** 시스템 도구를 제공하세요. 이 규칙에는 대상이 없습니다. 대화는 `transfer-to-agent` 결정으로 끝나고
시나리오가 발신자를 라우팅합니다. 전환 규칙은 API를 통해 구성합니다. 에이전트에 `PATCH`를 사용하여 도구를 추가하세요.

```bash
curl -X PATCH "https://api.elevenlabs.io/v1/convai/agents/agent_7101k5zvyjhmfg983brhmhkd98n6" \
  -H "xi-api-key: $ELEVENLABS_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "conversation_config": {"agent": {"prompt": {"built_in_tools": {
      "end_call": {"type": "system", "name": "end_call", "description": "",
                   "params": {"system_tool_type": "end_call"}},
      "transfer_to_number": {
        "type": "system", "name": "transfer_to_number", "description": "",
        "params": {
          "system_tool_type": "transfer_to_number",
          "transfers": [],
          "transfer_rules": [{
            "condition": "the caller asks to speak with a human",
            "provider_configs": [
              {"type": "voximplant", "config": {"type": "voximplant_handoff"}}
            ]
          }]
        }
      }
    }}}}
  }'
```

`end_call` 등 이미 보유한 도구를 포함해 에이전트의 전체 `built_in_tools` 객체를 전송하세요.
에이전트가 다시 보내는 옵션은 고정 토큰 `voximplant`이므로 도구당 Voximplant 규칙 하나면 충분합니다.
다른 공급자에 대해 구성한 전화번호와 SIP URI는 Voximplant 통화에서 제공되지 않습니다.

> **Note**
>
> 전환 규칙은 API로 관리되는 설정입니다. 이를 사용하는 에이전트의 **번호로 전환** 도구는 위와 같이 API를 통해
> 구성하고 업데이트하세요. 대시보드의 도구 편집기는 번호별 전환 목록과 함께 작동합니다.

#### 전용 API 키 생성

에이전트와 동일한 워크스페이스에서 API 키를 생성하고 ElevenAgents 범위로 지정하세요. Voximplant에서만 연결을
허용하려면 키의 허용 IP 주소를 Voximplant 미디어 서버로 제한하세요. Voximplant 미디어 서버 목록은
`https://api.voximplant.com/getMediaResources?with_mediaservers`에서 확인할 수 있습니다.

#### WebSocket URL 확인

시나리오는 에이전트 ID가 포함된 URL에 연결합니다.

| 환경       | URL                                                                                                               |
| -------- | ----------------------------------------------------------------------------------------------------------------- |
| 기본       | `wss://api.elevenlabs.io/v1/convai/conversation/voximplant/agent_7101k5zvyjhmfg983brhmhkd98n6`                    |
| 데이터 레지던시 | `wss://api.<region>.residency.elevenlabs.io/v1/convai/conversation/voximplant/agent_7101k5zvyjhmfg983brhmhkd98n6` |

에이전트의 브랜치를 고정하려면 `?branch_id=<branch_id>`를 추가하세요.

> **Info**
>
> ElevenLabs 계정이 격리된 레지던시 환경에 있다면 `<region>`을 지역 코드로 바꾸세요. 사용 가능한
> 지역은 [데이터 레지던시](/docs/ko/overview/administration/data-residency)를 참조하세요.

## Voximplant 설정

#### 애플리케이션 생성

[Voximplant 제어판](https://manage.voximplant.com/applications)에서 **Applications**를 열고
**Create application**을 클릭하세요. 예를 들어 `elevenlabs-agent`와 같은 이름을 지정하고
**Create**를 클릭하세요.

![Voximplant: 애플리케이션 생성](/docs/_fern-files/_img/29af341fa0a32eaaab444ade8305208121487413c5868597386a973b14fe4fe7.webp)

#### API 키를 시크릿으로 저장

애플리케이션에서 **Secrets**를 열고 **Add secret**을 클릭하세요. 이름을 `ELEVENLABS_API_KEY`로 지정하고,
에이전트 워크스페이스용으로 만든 API 키를 **Value**에 붙여 넣은 다음 **Add secret**을 클릭하세요.
시나리오는 `VoxEngine.getSecretValue()`로 이를 읽으므로 키가 시나리오 코드에 표시되지 않습니다.

![Voximplant: 시크릿 추가](/docs/_fern-files/_img/ecf8be823bbc15b263e3f4838684a78de3a3594704be0055b443d15ab93e1485.webp)

#### 시나리오 생성

**Scenarios**를 열고 **Create scenario**를 클릭한 다음 이름을 `elevenlabs_agent`로 지정하세요. 아래 코드를 붙여 넣고,
`ELEVENLABS_URL`의 에이전트 ID를 바꾼 후 **Save**를 클릭하세요. 이 시나리오는 수신 전화를 받고 에이전트로 스트리밍하며,
대화가 끝나면 결정에 따라 동작합니다. 발신 통화의 경우 전화를 건 `CallEvents.Connected` 핸들러에서 WebSocket을 여세요.

```javascript maxLines=0
const ELEVENLABS_URL =
  "wss://api.elevenlabs.io/v1/convai/conversation/voximplant/agent_7101k5zvyjhmfg983brhmhkd98n6";

VoxEngine.addEventListener(AppEvents.CallAlerting, ({ call }) => {
  call.answer();
  let decision = null;

  const ws = VoxEngine.createWebSocket(ELEVENLABS_URL, {
    headers: [
      {
        name: "Authorization",
        value: `Bearer ${VoxEngine.getSecretValue("ELEVENLABS_API_KEY")}`,
      },
    ],
  });

  ws.addEventListener(WebSocketEvents.OPEN, () => {
    call.sendMediaTo(ws, {
      encoding: WebSocketAudioEncoding.PCM16_8KHZ,
      customParameters: { customer_name: "Ana", task_id: "task-7" },
    });
    ws.sendMediaTo(call);
  });

  ws.addEventListener(WebSocketEvents.MESSAGE, ({ text }) => {
    const message = JSON.parse(text);
    if (message.customEvent === "elevenlabs-decision") {
      decision = message.payload;
      Logger.write(`ElevenLabs decision: ${JSON.stringify(decision)}`);
    }
  });

  ws.addEventListener(WebSocketEvents.CLOSE, (e) => {
    Logger.write(`ElevenLabs socket closed: code=${e.code} reason=${e.reason}`);
    if (decision?.action === "transfer-to-agent") {
      // Route the caller to a human here, for example with VoxEngine.callPSTN().
      call.say("Connecting you to an agent.", { language: VoiceList.Amazon.en_US_Joanna });
      call.addEventListener(CallEvents.PlaybackFinished, () => call.hangup());
      return;
    }
    call.hangup();
  });

  call.addEventListener(CallEvents.Disconnected, () => {
    ws.close();
    VoxEngine.terminate();
  });
});
```

![Voximplant: 편집기의 시나리오](/docs/_fern-files/_img/d2e26165f57cae40fc01e8de28052fef09b2f98bd90c0e1a8779423715f61e2e.webp)

#### 통화를 시나리오로 라우팅

**Routing**을 열고 **Create rule**을 클릭한 다음 이름을 지정하고 `elevenlabs_agent`
시나리오를 할당하세요. 기본 패턴 `.*`는 애플리케이션으로 들어오는 모든 통화를 시나리오로 전송합니다.
애플리케이션에서 다른 통화도 처리하는 경우 특정 전화번호로 범위를 좁히세요. 전화번호를 라우팅하려면 **Numbers**에서
전화번호를 애플리케이션에 연결하세요.

![Voximplant: 규칙 생성](/docs/_fern-files/_img/bc6830b7dcd66dc0b59595456b1197bdcbb9174f15778da7ba9655ba4b93936e.webp)

#### 테스트 통화하기

전화번호 없이도 브라우저에서 애플리케이션으로 전화를 걸 수 있습니다.

1. **Users**를 열고 **Create user**를 클릭한 다음 비밀번호가 있는 사용자를 생성하세요.

   ![Voximplant: 새 사용자](/docs/_fern-files/_img/4754c9234588fd0caa336e1168e207b265557dc4e6a12b1cf8bced1275b8bae6.webp)

2. **Routing**에서 **Test tools**, **Softphone** 순으로 클릭하세요. 해당 사용자로 로그인하고 마이크 접근을 허용한 뒤,
   아무 대상이나 입력하여 통화하세요. 에이전트가 첫 메시지로 응답합니다.

3. 통화가 끝나면 **Call history**를 여세요. 각 통화에는 통화 자체 옆에 WebSocket 레그가 표시됩니다.
   **View log**를 클릭하여 세션을 확인하세요.

   ![Voximplant: 통화 기록](/docs/_fern-files/_img/4e2416ad3587bb320300173cf679f7c5d7a5e545550b0a647062a79866dac40c.webp)

   로그에는 ElevenLabs가 전송한 결정, WebSocket 종료, 그리고 시나리오가 이후 수행한 동작이 표시됩니다.
   이 예에서 발신자는 상담원을 요청했으므로 에이전트가 제공한 사유와 함께 `transfer-to-agent`
   결정이 내려집니다.

   ![Voximplant: elevenlabs-decision 이벤트가 포함된
   세션 로그](/docs/_fern-files/_img/f8b4602fa02cfd3c417557377aaa5dd66e2e88a67e7adf4cb037ccae1840f45c.webp)

> **Warning**
>
> `MEDIA_ENDED`가 아니라 `elevenlabs-decision` 이벤트에 따라 동작하세요. Voximplant는 ElevenLabs에서 오디오가
> 약 1초 동안 수신되지 않으면 `MEDIA_ENDED`를 발생시키며, 이는 대화 중간에 일어날 수 있습니다.

## 동적 변수

`customParameters`의 모든 키는 동일한 이름의 동적 변수가 됩니다. 문자열이 아닌 값은 텍스트로 변환됩니다.
예를 들어 `true`는 `"true"`가 됩니다. `customParameters`는 객체 또는 JSON으로 인코딩된 문자열일 수 있습니다.
`system__` 또는 `integration__`로 시작하는 키는 예약되어 있으며 삭제됩니다.

## 결정 이벤트

대화가 끝나면 ElevenLabs는 `stop` 메시지 전에 정확히 하나의 결정을 전송합니다.

```json
{ "customEvent": "elevenlabs-decision", "payload": { "action": "terminate", "reason": "under_18" } }
```

| `action`            | 전송되는 경우                                                                                     | `reason`                              |
| ------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------- |
| `transfer-to-agent` | 에이전트가 Voximplant 전환 규칙과 함께 **번호로 전환**을 호출한 경우                                               | 에이전트가 전환을 위해 제공한 사유                   |
| `timeout`           | 무음 상태에서, 에이전트의 마지막 메시지 후 대화가 타임아웃되었거나, 에이전트가 말한 시간을 제외하고 발신자가 15초간 무음 상태여서 에이전트가 통화를 종료한 경우 | 타임아웃 메시지 또는 에이전트가 제공한 사유              |
| `terminate`         | 에이전트가 **통화 종료**로 통화를 종료했거나 대화가 정상적으로 끝난 경우                                                  | 사유가 있는 경우, 에이전트가 **통화 종료**와 함께 제공한 사유 |
| `unexpected`        | 오류로 인해 대화가 종료된 경우                                                                           | 오류 설명                                 |

예를 들어 발신자가 전화를 끊어 시나리오가 먼저 `stop`을 보내거나 WebSocket을 닫으면 ElevenLabs는 결정을 전송하지 않습니다.

## 오디오

Voximplant와 ElevenLabs는 양방향으로 8 kHz, 16비트 모노 리니어 PCM을 교환합니다. ElevenLabs는 에이전트 오디오를
실시간에 가깝게 전송하므로 발신자가 에이전트의 말을 끊어도 중단된 응답은 최대 약 0.4초만 더 재생됩니다.

## 인증 오류

`Authorization` 헤더가 없거나 형식이 잘못된 경우, API 키가 유효하지 않거나 연결 IP 주소에서 허용되지 않는 경우,
키가 다른 워크스페이스에 속하는 경우 또는 키의 사용자가 인증이 활성화된 에이전트에 접근할 수 없는 경우
ElevenLabs는 코드 `1008` 및 사유 `bad auth token`으로 WebSocket을 닫습니다.

## 제한 사항 및 지원되지 않는 기능

* 클라이언트 도구와 **키패드 터치톤 재생** 시스템 도구는 지원되지 않습니다. **번호로 전환**은 Voximplant 전환 규칙을
  통해서만 작동하며, 발신자의 연결 대상은 시나리오가 결정합니다.
* 다른 첫 메시지와 같은 구성 재정의는 시나리오에서 전달할 수 없으며 동적 변수만 전달할 수 있습니다.
* 데이터 수집 결과는 시나리오로 전송되지 않습니다. 결과는
  [통화 후 웹훅](/docs/ko/eleven-agents/workflows/post-call-webhooks)을 사용하세요.