ClawOps Docs

VoiceML

XML 기반 통화 제어 마크업 언어. 요청/응답 형식과 Say, Play, Gather, Record, Dial, Connect, Hangup, Redirect 태그 레퍼런스.

개요

ClawOps 번호로 수신 전화가 들어오면, 플랫폼은 설정된 웹훅 URL로 HTTP 요청을 전송합니다. 서버는 VoiceML(XML)로 응답하여 통화 흐름을 제어합니다.

VoiceML은 TwiML 호환 형식으로, 기존 TwiML 기반 코드를 최소한의 수정으로 사용할 수 있습니다.

요청 형식

POST 요청 (기본) — Content-Type: application/x-www-form-urlencoded 파라미터가 요청 본문(body)으로 전송됩니다.

GET 요청 — 파라미터가 쿼리 문자열(query string)으로 전송됩니다.

POST와 GET 모두 X-Signature 헤더가 포함됩니다. 서명 검증을 통해 요청의 무결성을 확인할 수 있습니다.

요청 파라미터

파라미터타입필수설명
CallIdstring필수통화 고유 ID (예: CA1a2b3c...)
AccountIdstring필수계정 ID (예: AC...)
Fromstring필수발신 번호 (예: 01012345678)
Tostring필수수신 번호 — ClawOps 번호 (예: 07012340001)
CallStatusstring필수통화 상태 (예: in-progress)
Directionstring필수통화 방향 (예: inbound)

POST vs GET

POST https://your-server.com/voice HTTP/1.1
Content-Type: application/x-www-form-urlencoded
X-Signature: abc123...

CallId=CA...&AccountId=AC...&From=010...&To=070...&CallStatus=in-progress&Direction=inbound
GET https://your-server.com/voice?CallId=CA...&AccountId=AC...&From=010...&To=070...&CallStatus=in-progress&Direction=inbound HTTP/1.1
X-Signature: abc123...

응답 형식

웹훅 서버는 Content-Type: application/xml 헤더와 함께 VoiceML XML을 반환해야 합니다. 모든 응답은 <Response> 루트 엘리먼트로 시작합니다.

<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say language="ko">안녕하세요.</Say>
  <Gather timeout="5" action="https://your-server.com/gather">
    <Say language="ko">서비스를 선택하세요. 1번 상담, 2번 안내</Say>
  </Gather>
  <Say language="ko">응답이 없습니다.</Say>
  <Hangup/>
</Response>

동사 레퍼런스

<Say>

텍스트를 음성으로 읽어줍니다 (TTS).

파라미터타입필수설명
languagestring선택음성 언어 (ko, en, ja). 기본값: ko
voicestring선택음성 종류

language 속성은 ko(한국어), en(영어), ja(일본어)를 지원합니다. 생략 시 한국어가 기본 적용됩니다.

<Say language="ko">안녕하세요. 고객센터입니다.</Say>

<Play>

음성 파일을 재생합니다.

파라미터타입필수설명
urlstring필수재생할 오디오 파일 URL
<Play url="https://example.com/welcome.wav"/>

<Gather>

DTMF 입력(키패드)을 수집합니다. <Say>, <Play>를 중첩할 수 있습니다.

파라미터타입필수설명
timeoutinteger선택입력 대기 시간 (초). 기본값: 5
numDigitsinteger선택수집할 자릿수
actionstring선택입력 완료 후 요청할 콜백 URL
methodstring선택action URL의 HTTP 메서드. 기본값: POST
<Gather timeout="5" numDigits="1" action="/handle-input" method="POST">
  <Say language="ko">1번 상담, 2번 안내, 3번 기타</Say>
</Gather>

Gather 완료 시 action URL로 요청이 전송되며, Digits 파라미터에 사용자 입력값이 포함됩니다.

<Record>

통화 내용을 녹음합니다.

파라미터타입필수설명
maxLengthinteger선택최대 녹음 시간 (초). 기본값: 3600
actionstring선택녹음 완료 후 요청할 URL
methodstring선택action의 HTTP 메서드. 기본값: POST
<Record maxLength="60" action="https://your-server.com/recording"/>

<Dial>

외부 번호로 전화를 연결합니다. 하위에 <Number> 또는 <Sip>을 사용합니다.

파라미터타입필수설명
callerIdstring선택발신자 번호
timeoutinteger선택연결 대기 시간 (초). 기본값: 30
<Dial callerId="07012340001" timeout="30">
  <Number>01012345678</Number>
</Dial>

SIP 연결:

<Dial>
  <Sip>sip:user@domain.com</Sip>
</Dial>

<Connect>

WebSocket Stream을 연결하여 실시간 양방향 오디오를 처리합니다. AI Agent 연동에 주로 사용됩니다.

하위에 <Stream> 엘리먼트를 포함하며, <Stream><Parameter> 자식을 가질 수 있습니다.

Stream 속성

파라미터타입필수설명
urlstring필수WebSocket 서버 URL (wss://)
trackstring선택스트리밍할 오디오 트랙 (inbound, outbound, both)

Parameter 속성

파라미터타입필수설명
namestring필수파라미터 이름
valuestring필수파라미터 값
<Connect>
  <Stream url="wss://your-server.com/stream" track="inbound">
    <Parameter name="userId" value="123"/>
    <Parameter name="language" value="ko"/>
  </Stream>
</Connect>

Stream 프로토콜에 대한 자세한 내용은 Stream WebSocket 문서를 참고하세요.

<Hangup>

통화를 종료합니다. 속성이 없습니다.

<Hangup/>

<Redirect>

다른 URL로 요청을 리다이렉트하여 새로운 VoiceML을 가져옵니다. 엘리먼트 텍스트에 URL을 포함합니다.

파라미터타입필수설명
methodstring선택HTTP 메서드. 기본값: POST
<Redirect method="POST">https://your-server.com/next-step</Redirect>

중첩 규칙

부모 엘리먼트허용되는 자식 엘리먼트
<Gather><Say>, <Play>
<Dial><Number>, <Sip>
<Connect><Stream>
<Stream><Parameter>
<Say>, <Play>, <Record>, <Hangup>, <Redirect>중첩 불가

중첩 규칙을 따르지 않는 XML은 파싱 오류가 발생합니다. 각 동사에 허용된 자식 엘리먼트만 사용하세요.

서버 구현 예제

Webhook 엔드포인트를 구현하여 전화를 수신하고 VoiceML로 응답하는 예제입니다.

Python (Flask)

from flask import Flask, request

app = Flask(__name__)

@app.route("/voice", methods=["POST"])
def handle_call():
    call_id = request.form.get("CallId")
    from_number = request.form.get("From")
    return """<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say language="ko">안녕하세요. ClawOps에 연결되었습니다.</Say>
  <Hangup/>
</Response>""", 200, {"Content-Type": "application/xml"}

로컬 개발 시 ngrok 등의 터널링 도구를 사용하면 외부에서 로컬 서버로 Webhook을 전달받을 수 있습니다. Webhook 없이 AI 에이전트로 전화를 처리하려면 Voice Agent를 참고하세요.

전체 예제: IVR 시나리오

<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say language="ko">안녕하세요. 고객센터에 연결되었습니다.</Say>
  <Gather timeout="5" numDigits="1" action="/handle-input" method="POST">
    <Say>상품 문의는 1번, 배송 조회는 2번, 상담원 연결은 3번을 눌러주세요.</Say>
  </Gather>
  <Say>응답이 없습니다. 상담원에게 연결합니다.</Say>
  <Dial callerId="07012340001" timeout="30">
    <Number>01012345678</Number>
  </Dial>
</Response>