AI 전화 통화 API: AI 에이전트로 실제 전화를 걸고 받기
Wixzel Voice는 자체 SIP 트렁크로 실제 전화를 걸고 받거나 웹·모바일 앱에서 사람들과 대화하는 AI 에이전트를 만들기 위한 음성 AI API입니다. API 키 하나와 선불 잔액 하나로 모든 음성 엔진을 사용하며, 실제 사용량에 따라 초 단위로 과금됩니다.
최종 업데이트:
AI 전화 통화를 위한 Wixzel Voice API란 무엇인가요?
Wixzel Voice API를 쓰면 직접 정의한 AI 에이전트가 자체 SIP 트렁크로 실제 전화를 걸고 받습니다. 번호와 에이전트 ID로 POST /v1/calls를 호출하며, 실제 사용량만큼 초 단위로 과금됩니다.
한 줄로 말하면 Wixzel Voice는 AI 전화 에이전트를 만들기 위한 음성 AI API입니다. 에이전트는 직접 정의하고 통화는 Wixzel Voice가 처리하는 AI 통화 API입니다. 발신은 POST /v1/calls가 번호로 전화를 걸어 에이전트를 연결합니다. 수신은 inbound_agent_id가 설정된 전화번호가 그 번호로 오는 모든 전화를 받습니다. 같은 에이전트가 실시간 API를 통해 전화 회선 없이 웹 페이지나 앱에서 사람들과 대화하거나, MCP 서버를 통해 AI 어시스턴트의 지시로 움직일 수도 있습니다.
Wixzel Voice의 방식은 ‘API 키 하나, 잔액 하나, 모든 음성 엔진’입니다. 음성 인식, 언어, 음성 제공업체 계정은 플랫폼의 것이므로 Deepgram, ElevenLabs, OpenRouter 키를 가져올 필요가 없습니다. 직접 가져오는 것은 전화 회선 하나뿐입니다(자체 SIP 트렁크 사용). Twilio, Telnyx, Plivo 또는 모든 SIP 제공업체의 트렁크를 쓸 수 있습니다.
Wixzel Voice로 AI 전화 통화를 어떻게 거나요?
API 키와 크레딧이 준비된 뒤 요청 네 개면 됩니다. 각 응답에는 다음 요청에 필요한 ID가 들어 있으며, 아래 코드는 그 값을 셸 변수에서 읽습니다.
- 01
SIP 트렁크 연결
통신사의 호스트와 자격 증명입니다. 비밀번호는 암호화되어 저장되며 다시 반환되지 않습니다. 응답에는 통신사가 허용 목록에 추가해야 하는
platform_ip(현재187.126.120.231)와 통신사가 수신 전화를 보내는 곳인origination_uri(현재sip:187.126.120.231:5090)가 들어 있습니다.cURLcurl https://api.voice.wixzel.com/v1/sip-trunks \ -H "Authorization: Bearer $WIXZEL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "My carrier", "host": "sip.carrier.example", "username": "acct123", "password": "..." }' - 02
트렁크에 전화번호 등록
그 통신사에서 이미 가지고 있는 번호를 E.164 형식으로 등록합니다. Wixzel Voice는 번호를 판매하거나 이동하지 않습니다. 트렁크가 Twilio, Telnyx, Plivo, Vobiz, Vonage 계정에 연결되어 있다면 대신
POST /v1/phone-numbers/purchase로 그 계정에서 새 번호를 구매할 수 있습니다. 요금은 통신사가 자체 가격으로 청구합니다.cURLcurl https://api.voice.wixzel.com/v1/phone-numbers \ -H "Authorization: Bearer $WIXZEL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "phone_number": "+14155550100", "sip_trunk_id": "'"$TRUNK_ID"'" }' - 03
에이전트 만들기
말하듯 쓴 짧은 프롬프트, 처음 하는 말, 그리고 음성(모델 세 개 또는 실시간 모델 하나)입니다.
opening_message는 필수입니다. 없으면 에이전트가 전화를 받고도 아무 말도 하지 않기 때문입니다.outbound_phone_number_id는 2단계의 번호를 발신자 번호로 지정합니다.cURLcurl https://api.voice.wixzel.com/v1/agents \ -H "Authorization: Bearer $WIXZEL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Support", "system_prompt": "You are a concise support agent.", "opening_message": "Hi, how can I help?", "voice": { "stt": { "model": "deepgram/nova-3" }, "llm": { "model": "openrouter/gpt-4o-mini" }, "tts": { "model": "elevenlabs/eleven_flash_v2_5" } }, "outbound_phone_number_id": "'"$NUMBER_ID"'" }' - 04
전화 걸기
수신 번호와 에이전트를 지정합니다. 네트워크 시간 초과만으로는 통화가 걸렸는지 알 수 없으므로
Idempotency-Key가 필수입니다. 같은 키로 다시 시도하면 두 번 전화를 거는 대신 원래 통화를 반환합니다. 응답은201과status: "queued"상태의 통화입니다.cURLcurl https://api.voice.wixzel.com/v1/calls \ -H "Authorization: Bearer $WIXZEL_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "to": "+14155551234", "agent_id": "'"$AGENT_ID"'" }'
첫 통화가 연결되기 전에 통신사에서 platform_ip를 허용 목록에 추가해야 합니다. Twilio, Telnyx, Plivo, Vonage, Bandwidth, Exotel, Vobiz, Mobily에서 그 설정이 어디 있는지는 통신사 가이드에 나와 있습니다.
TypeScript나 Dart로 AI 전화 통화를 어떻게 거나요?
공식 SDK는 Node, 브라우저, 엣지 런타임에서 TypeScript와 JavaScript용인 npm의 wixzel-voice, 그리고 Dart와 Flutter용인 pub.dev의 wixzel_voice입니다. 두 SDK 모두 모든 엔드포인트를 지원하고, 속도 제한에 걸리면 다시 시도하며, 비용이 드는 경로에서는 Idempotency-Key를 생성합니다. 다른 언어는 일반 HTTP API와 OpenAPI 문서를 사용합니다.
import { WixzelVoice } from 'wixzel-voice';
const client = new WixzelVoice({ apiKey: process.env.WIXZEL_API_KEY! });
// 1. Connect your SIP trunk.
const trunk = await client.sipTrunks.create({
name: 'My carrier',
host: 'sip.carrier.example',
username: 'acct123',
password: process.env.CARRIER_PASSWORD!,
});
console.log('Allowlist', trunk.platform_ip, 'and send inbound calls to', trunk.origination_uri);
// 2. Register a number you own on it.
const number = await client.phoneNumbers.create({
phone_number: '+14155550100',
sip_trunk_id: trunk.id,
});
// 3. Create the agent, with that number as its caller ID.
const agent = await client.agents.create({
name: 'Support',
system_prompt: 'You are a concise support agent.',
opening_message: 'Hi, how can I help?',
voice: {
stt: { model: 'deepgram/nova-3' },
llm: { model: 'openrouter/gpt-4o-mini' },
tts: { model: 'elevenlabs/eleven_flash_v2_5' },
},
outbound_phone_number_id: number.id,
});
// 4. Place the call. The SDK sends the Idempotency-Key for you.
const call = await client.calls.create({ to: '+14155551234', agent_id: agent.id });
// Inbound: calls to the number are answered by the same agent.
await client.phoneNumbers.update(number.id, { inbound_agent_id: agent.id });
// Later: what happened, what it cost, what was said.
const detail = await client.calls.retrieve(call.id);
console.log(detail.status, detail.cost_micros, detail.failure_code, detail.failure_reason);
for (const line of detail.transcript) console.log(line.role, line.content);import 'dart:io';
import 'package:wixzel_voice/wixzel_voice.dart';
Future<void> main() async {
final client = WixzelVoice(apiKey: Platform.environment['WIXZEL_API_KEY']!);
// 1. Connect your SIP trunk.
final trunk = await client.sipTrunks.create(CreateSipTrunk(
name: 'My carrier',
host: 'sip.carrier.example',
username: 'acct123',
password: Platform.environment['CARRIER_PASSWORD']!,
));
print('Allowlist ${trunk.platformIp}; send inbound calls to ${trunk.originationUri}');
// 2. Register a number you own on it.
final number = await client.phoneNumbers.create(CreatePhoneNumber(
phoneNumber: '+14155550100',
sipTrunkId: trunk.id,
));
// 3. Create the agent, with that number as its caller ID.
final agent = await client.agents.create(CreateAgent(
name: 'Support',
systemPrompt: 'You are a concise support agent.',
openingMessage: 'Hi, how can I help?',
voice: VoiceConfig.composed(
stt: const SttConfig(model: 'deepgram/nova-3'),
llm: const LlmConfig(model: 'openrouter/gpt-4o-mini'),
tts: const TtsConfig(model: 'elevenlabs/eleven_flash_v2_5'),
),
outboundPhoneNumberId: number.id,
));
// 4. Place the call. The SDK sends the Idempotency-Key for you.
final call = await client.calls.create(CreateCall(
to: '+14155551234',
agentId: agent.id,
));
// Inbound: calls to the number are answered by the same agent.
await client.phoneNumbers.update(
number.id, UpdatePhoneNumber(inboundAgentId: agent.id));
// Later: what happened, what it cost, what was said.
final detail = await client.calls.retrieve(call.id);
print('${detail.status} ${detail.costMicros} ${detail.failureCode}');
for (final line in detail.transcript) {
print('${line.role}: ${line.content}');
}
client.close();
}Wixzel Voice에서 AI 에이전트는 수신 전화를 어떻게 받나요?
전화번호에 inbound_agent_id를 지정하면 그 번호로 오는 모든 전화를 해당 에이전트가 받습니다. 지정하지 않으면 그 번호로 오는 수신 전화는 거부됩니다.
curl -X PATCH https://api.voice.wixzel.com/v1/phone-numbers/$NUMBER_ID \
-H "Authorization: Bearer $WIXZEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "inbound_agent_id": "'"$AGENT_ID"'" }'나머지 절반은 통신사 쪽 설정입니다. 수신 전화가 트렁크의 origination_uri(현재 sip:187.126.120.231:5090)로 전달되어야 합니다. 이는 허용 목록과는 통신사 콘솔의 다른 곳에 있는 별도 설정이며, 빠져 있으면 번호가 그냥 울리지 않습니다. 통신사가 전화를 보낼 곳이 없으므로 어떤 로그에도 아무것도 남지 않습니다. 모든 수신 통화는 direction: "inbound"와 양쪽 번호와 함께 기록되며 inboundCall 웹훅을 보냅니다.
Wixzel Voice 통화 기록은 무엇을 반환하나요?
POST /v1/calls는 status: "queued" 상태의 통화를 바로 반환합니다. GET /v1/calls/{id}는 통화 중과 통화가 끝난 뒤 전체 기록을 반환하며, X-Wixzel-Signature로 서명된 callCompleted 웹훅이 통화가 끝났음을 서버에 알려 주므로 폴링할 필요가 없습니다.
| 필드 | 담긴 내용 |
|---|---|
| status | queued, ringing, in-progress, completed, failed, busy, no-answer 또는 canceled. |
| direction / channel | outbound 또는 inbound. 트렁크를 통한 통화는 phone, 브라우저나 앱의 실시간 세션은 web입니다. |
| duration_seconds | 통화가 끝난 뒤의 통화 시간. |
| engine | 에이전트가 사용한 음성 엔진. |
| cost_micros | 통화 비용을 마이크로 USD(1,000,000 = $1.00)로 표시하며, 통화가 정산될 때 기록됩니다. 연결되지 않은 통화는 null이며 비용이 들지 않습니다. |
| transcript | 모든 발화 차례를 role(user, assistant 또는 system), content, UTC 타임스탬프와 함께 담습니다. GET /v1/calls/{id}/transcript에서도 볼 수 있습니다. |
| summary / recording_url | 통화가 녹음된 경우 녹음 파일. API로 건 통화의 summary는 비어 있으므로 대화 기록을 읽으세요. |
| failure_code / failure_reason | 연결되지 않은 통화의 경우: 통신사의 Q.850 원인 코드(분기 처리에 쓸 수 있는 안정된 값)와 해결 방법을 쉬운 말로 설명한 내용. |
| transfers | 발신자를 사람에게 연결하려 한 시도마다 하나씩, 상태, 벨 울린 시간, SIP 원인과 함께 기록됩니다. |
| metadata | POST /v1/calls에 첨부한 평면 문자열 맵을 그대로 반환합니다. 모델에는 절대 보내지 않습니다. |
cost_micros의 모든 마이크로 달러는 GET /v1/usage/events의 사용량 행으로 설명됩니다. 인식한 음성의 초, 모델의 입력·출력 토큰, 합성한 문자 수, 분당 오케스트레이션 수수료입니다.
AI 전화 통화가 연결되지 않으면 어떻게 되나요?
통신사가 거부한 통화는 status: "failed"(또는 busy, no-answer), failure_code, failure_reason, cost_micros: null을 가집니다. 비용이 들지 않았고, 그 통화를 위해 예약했던 크레딧은 해제되었습니다. failure_code는 통신사의 Q.850 원인 코드로 분기 처리에 쓸 수 있는 안정된 값이며, failure_reason은 해결 방법을 쉬운 말로 알려 줍니다.
일부 요청은 전화를 걸기 전에 거부되며 비용이 들지 않습니다. 잔액이 처음 15초를 감당하지 못하면 402 insufficient_credits, Idempotency-Key가 없는 직접 요청은 400 missing_idempotency_key, 동시 통화 한도를 넘는 통화는 503으로 거부됩니다. 모든 실패 코드와 거부 사유, 그리고 각각의 대처 방법은 통신사 페이지에 있습니다.
Wixzel Voice에서 AI 전화 통화 비용은 얼마인가요?
통화 한 건의 엔진 비용을 일반적인 발화 속도 기준으로, 요금 측정에 쓰이는 가격표에서 계산했습니다. 각 가격에는 분당 $0.012의 오케스트레이션 수수료가 포함되어 있습니다. 통화는 실제 사용량만큼 초 단위로 과금되므로 90초 통화는 3분 통화의 약 절반입니다. 전화 통신은 자체 트렁크를 통신사 요금으로 사용하며 포함되지 않습니다.
| 엔진 | 파이프라인 | 1분 통화 | 3분 통화 | 5분 통화 |
|---|---|---|---|---|
| classic | Deepgram + GPT-4o-mini + ElevenLabs | $0.0867 | $0.2601 | $0.4335 |
| sarvam | Sarvam, 처음부터 끝까지 인도 언어 | $0.0617 | $0.1851 | $0.3085 |
| gemini-live | Gemini Live, 네이티브 오디오 | $0.0491 | $0.1473 | $0.2455 |
| deepgram-agent | Deepgram Voice Agent | $0.1851 | $0.5553 | $0.9255 |
선불입니다. $5부터 크레딧을 충전하며, 구독도 무료 요금제도 없습니다. 현재 가장 저렴한 엔진은 분당 $0.0491인 gemini-live입니다. 내 사용량으로 견적 내기.
AI 전화 통화를 위한 Wixzel Voice API의 한계는 무엇인가요?
- Wixzel Voice는 배포되어 실제 통화를 처리하고 있습니다. 동작 변경은 날짜가 붙은
Wixzel-Version헤더로 구분해 배포하며, 기존 키는 만들어질 때의 동작을 유지합니다. - 기본적으로 계정당 동시 통화 5건이며, 요청하면 상향됩니다. 웹·앱 세션도 같은 한도에 포함됩니다.
- 사람에게 연결하는 기능은 블라인드 방식만 지원합니다. 발신자는 등록된 번호로 바로 연결됩니다. 상담 후 연결(attended transfer)은 구현되지 않았고, 테스트 통화와 웹·앱 세션에서는 연결 기능을 쓸 수 없습니다.
- Wixzel Voice는 전화번호를 판매하거나 이동하지 않으며, 전화 통화에는 자체 SIP 트렁크가 필요합니다. 자체 Twilio, Telnyx, Plivo, Vobiz, Vonage 계정에서 번호를 대신 구매할 수 있으며, 요금은 통신사가 청구합니다.
- 웹·앱 세션은 G.711 µ-law 8 kHz 오디오를 사용합니다. 광대역이 아니라 전화 음질입니다.
- 키당 초당 API 요청 10건, 버스트 20건입니다. 속도 제한에 걸린 요청은 청구되지 않습니다.
- API와 저장 데이터는 인도 뭄바이에 있습니다. 미국과 인도의 음성·언어 제공업체가 통화 중에 오디오와 텍스트를 처리하며, sarvam 엔진은 음성을 인도에서 처리합니다.
- 무료 요금제와 체험 크레딧이 없습니다. 최소 충전 금액은 $5입니다. 유일한 무료 사용은 계정당 5분간 콘솔에서 자기 에이전트와 대화하는 것입니다.
자주 묻는 질문
AI 전화 통화 API란 무엇인가요?
POST /v1/calls가 자체 SIP 트렁크로 전화를 걸고, inbound_agent_id가 설정된 전화번호는 같은 방식으로 전화를 받습니다.Wixzel Voice는 AI 통화 API인가요, 완성된 통화 에이전트인가요?
AI 전화 통화 비용은 얼마인가요?
AI 에이전트가 발신뿐 아니라 수신 전화도 받을 수 있나요?
PATCH /v1/phone-numbers/{id}로 전화번호에 inbound_agent_id를 설정하면 그 번호로 오는 전화를 해당 에이전트가 받습니다. 통신사는 수신 전화를 트렁크가 반환한 origination_uri로 보내야 합니다. inbound_agent_id가 없으면 그 번호로 오는 수신 전화는 거부됩니다.AI 에이전트가 발신자를 사람에게 연결할 수 있나요?
human_transfer에 등록한 연결 대상 중 하나로 넘기며, 전화는 자체 SIP 트렁크로 걸립니다. 에이전트가 먼저 상대와 통화하는 상담 후 연결은 구현되지 않았고, 테스트 통화와 웹·앱 세션에서는 연결 기능을 쓸 수 없습니다.AI 통화는 한 번에 몇 건까지 할 수 있나요?
무료 체험이 있나요?
402 insufficient_credits로 거부됩니다.
