Wixzel Voice로 Node.js에서 Twilio AI 접수원 만들기
이 가이드를 마치면 기존 Twilio 번호로 오는 전화를 직접 만든 AI 접수원이 받습니다. 접수원은 발신자에게 인사하고, 프롬프트를 바탕으로 답하고, API로 다시 조회할 수 있는 예약을 잡습니다. Node.js에서 TypeScript·JavaScript용 wixzel-voice SDK와 Twilio Elastic SIP 트렁크로 약 30분이 걸립니다. classic 엔진의 엔진 사용 요금은 연결된 1분당 $0.0867, 3분 통화에 약 $0.26이며, 선불 잔액에서 초 단위로 청구됩니다. 트렁크 사용 시간은 Twilio가 마진 없이 직접 청구합니다.
작성 Aqeel Shamsudheen · 게시 · 업데이트
- 소요 시간
- 약 30분
- 엔진
- classic
- 엔진 사용 시간
- 연결된 1분당 $0.0867
시작하기 전에 무엇이 필요한가요?
- Elastic SIP Trunking을 사용할 수 있는 Twilio 계정과 Twilio 전화번호.
- Wixzel Voice API 키(
wv_live_…)와 계정의 크레딧. 최소 충전 금액은 $5입니다. - Node.js 20 이상.
Wixzel Voice에서 단계별로 어떻게 만드나요?
01Twilio에서 Elastic SIP 트렁크 만들기
Twilio 콘솔에서 Elastic SIP Trunking을 열고 트렁크를 만듭니다. Termination에서
your-clinic.pstn.twilio.com같은 Termination SIP URI를 정합니다. 이 호스트 이름이 다음 단계에서 연결할 트렁크의host입니다.Twilio 콘솔은 열어 둡니다. API가 입력할 주소 두 개를 알려 주면 다시 돌아옵니다.
02SDK 설치하고 트렁크 연결하기
SDK를 설치하고 API 키를 환경 변수에 넣습니다.
shellnpm install wixzel-voice export WIXZEL_API_KEY="wv_live_..."trunk.mjsimport { WixzelVoice } from 'wixzel-voice'; const client = new WixzelVoice({ apiKey: process.env.WIXZEL_API_KEY }); const trunk = await client.sipTrunks.create({ name: 'Twilio', host: 'your-clinic.pstn.twilio.com', // your Termination SIP URI port: 5060, transport: 'udp', }); console.log('TRUNK_ID', trunk.id); console.log('Allowlist at Twilio:', trunk.platform_ip); console.log('Origination URI:', trunk.origination_uri);Twilio는 발신 IP로 인증하므로 여기에는 사용자 이름이나 비밀번호가 없습니다. IP 설정은 다음 단계에서 합니다. 대신 Twilio Credential List를 쓰려면 그
username과password를 넘깁니다. 비밀번호는 암호화되어 저장되며 API가 다시 반환하지 않습니다.03Twilio가 양방향으로 Wixzel Voice를 가리키게 하기
Twilio는 발신과 수신을 따로 설정하며, 어느 쪽이든 빠져 있으면 아무 표시 없이 실패합니다.
Termination의 Authentication에서 위에서 출력된
platform_ip를 IP Access Control List에 추가합니다. 이 설정이 없으면 Twilio가 약 1초 만에 hangup cause 21로 통화를 거부합니다.Origination에서 위에서 출력된
origination_uri를 값으로 하는 Origination SIP URI를 추가합니다. 이 설정이 없으면 번호로 온 전화가 에이전트에 도달하지 않고, 통화가 Twilio를 벗어나지 않으므로 양쪽 로그에 아무것도 남지 않습니다.마지막으로 Numbers에서 Twilio 번호를 이 트렁크에 연결합니다.
04접수원 만들기
에이전트는 프롬프트, 첫 인사, 음성 엔진으로 이루어집니다.
appointment_booking_enabled를 켜면 통화 중에 예약을 조회, 생성, 취소할 수 있고, 어떤 요일과 시간에 몇 분 단위로 예약을 제안할지는 프롬프트에 적습니다.agent.mjsimport { WixzelVoice } from 'wixzel-voice'; const client = new WixzelVoice({ apiKey: process.env.WIXZEL_API_KEY }); const agent = await client.agents.create({ name: 'Reception', system_prompt: [ 'You are the receptionist for Riverside Dental, answering the phone.', 'Keep every reply to one or two short sentences, and never read out lists.', 'You can book, move and cancel check-ups and cleanings.', 'Offer appointments Monday to Friday, 09:00 to 17:00, 30 minutes each.', 'Before booking, ask for the full name of the caller and repeat the day and time back.', 'For a dental emergency, tell the caller to hang up and ring the emergency number.', 'If you cannot help, take a message and promise a callback.', ].join('\n'), opening_message: 'Riverside Dental, this is the automated assistant. How can I help?', appointment_booking_enabled: true, voice: { stt: { model: 'deepgram/nova-3', language: 'en-US' }, llm: { model: 'openrouter/gpt-4o-mini' }, tts: { model: 'elevenlabs/eleven_flash_v2_5' }, }, }); console.log('AGENT_ID', agent.id);이것이 classic 엔진입니다. Deepgram Nova-3, GPT-4o-mini, ElevenLabs Flash v2.5로 구성되며, 영어에서 가장 저렴하면서 안정적인 조합으로 분당 $0.0867입니다. 화자를 고르려면
tts블록에voice를 추가합니다. 사용할 수 있는 음성은client.engines.voices('classic')로 조회합니다.첫 인사에서 발신자에게 자동 응답 어시스턴트와 통화 중이라고 알립니다. 법적으로 이런 고지나 통화 녹음 고지가 필요한 경우 첫 인사에 넣는 것이 맞습니다.
05번호를 접수원에게 연결하기
트렁크에 번호를 등록하고, 그 번호로 오는 전화를 받을 에이전트를 지정합니다.
number.mjsimport { WixzelVoice } from 'wixzel-voice'; const client = new WixzelVoice({ apiKey: process.env.WIXZEL_API_KEY }); const number = await client.phoneNumbers.create({ phone_number: '+14155550100', // your Twilio number, in E.164 sip_trunk_id: process.env.TRUNK_ID, inbound_agent_id: process.env.AGENT_ID, }); console.log('NUMBER_ID', number.id);inbound_agent_id가 없으면 그 번호로 오는 전화는 거부됩니다. 같은 에이전트가 알림이나 테스트 통화처럼 발신도 하게 하려면client.agents.update(agentId, { outbound_phone_number_id: number.id })로 그 번호를 발신자 번호로 지정합니다.06전화를 걸어 보고 통화와 예약 확인하기
휴대폰으로 Twilio 번호에 전화를 걸어 예약을 잡아 봅니다. 전화를 끊으면 통화와 예약이 모두 API에 있습니다.
results.mjsimport { WixzelVoice } from 'wixzel-voice'; const client = new WixzelVoice({ apiKey: process.env.WIXZEL_API_KEY }); // Lists are newest first. const calls = await client.calls.list({ direction: 'inbound', limit: 1 }); const latest = calls.data[0]; if (latest) { const call = await client.calls.retrieve(latest.id); console.log(call.status, call.duration_seconds + 's', (call.cost_micros ?? 0) / 1e6, 'USD'); for (const line of call.transcript) console.log(line.role + ': ' + line.content); } const appointments = await client.appointments.list({ limit: 5 }); for (const a of appointments.data) { console.log(a.date_time, a.client_name, a.phone_number, a.status); }date_time은 항상 UTC입니다. 예약이 생길 때마다appointmentBooked웹훅도 발생하므로, 이를 이용해 예약을 실시간으로 자체 캘린더에 복사할 수 있습니다.
Wixzel Voice에서 이 빌드의 비용은 얼마인가요?
classic 엔진에서 연결된 1분당 $0.0867로 3분 통화를 할 때의 엔진 사용 시간 비용: 통화당 약 $0.26. 전화 통신은 자체 트렁크에서 통신사 요금으로 청구되며 포함되지 않습니다.
| 월 통화 수 | 연결된 시간(분) | 월 엔진 비용 |
|---|---|---|
| 200 | 600 | $52.02 |
| 1,000 | 3,000 | $260 |
| 3,000 | 9,000 | $780 |
Wixzel Voice는 엔진 사용 시간만 통화 초 단위로 선불 잔액에서 청구합니다. Elastic SIP Trunking 사용 시간은 Twilio가 사용자의 Twilio 요금으로 Twilio 계정에 청구하며, Wixzel Voice는 이 요금을 보지도 않고 마진을 붙이지도 않습니다.
선불이며 구독과 무료 등급이 없습니다. $5부터 크레딧을 충전하면 사용량만큼 차감됩니다. 내 사용량으로 추정해 보기.
이 빌드에는 Wixzel Voice의 어떤 한도가 적용되나요?
- 접수원은 영업시간을 프롬프트로 압니다. v1 API에는 예약 가능 시간 엔드포인트가 없어 실제 캘린더를 볼 수 없습니다. API로 이미 예약된 시간은 거부되지만, 다른 곳에서 관리하는 예약은 알지 못합니다.
- 예약은 계정 시간대로 읽히며, 변경하지 않았다면 UTC이고 v1 API에서는 아직 바꿀 수 없습니다. 처음 들어온 예약의
date_time을 확인한 뒤에 사용하시고, 사업장이 UTC 기준이 아니라면 aqeel@wixzel.com로 연락해 주십시오. - 계정당 기본적으로 동시에 5건의 통화를 진행할 수 있습니다. 그 이상의 통화는 하나가 끝날 때까지 거부되며, 요청하면 한도를 늘려 드립니다.
- 번호는 Twilio에 그대로 남습니다. Wixzel Voice는 번호를 판매, 이전하거나 보유하지 않습니다.
- Wixzel Voice의 동작 변경은 날짜 기반
Wixzel-Version헤더로 구분되어 배포됩니다.
자주 묻는 질문
Twilio로 AI 접수원을 만들 수 있나요?
inbound_agent_id와 함께 등록합니다. 그러면 번호로 오는 전화를 에이전트가 받고, 트렁크 요금은 계속 Twilio가 직접 청구합니다.번호를 Twilio에서 옮겨야 하나요?
Twilio 번호로 건 전화가 에이전트에 연결되지 않는 이유는 무엇인가요?
origination_uri와 일치해야 하고, 번호에는 inbound_agent_id가 있어야 합니다.AI 접수원 통화 비용은 얼마인가요?
JavaScript 외의 언어용 SDK도 있나요?
wixzel-voice와 Dart·Flutter용 pub.dev 패키지 wixzel_voice입니다. 다른 언어에서는 https://docs.voice.wixzel.com/api-reference의 OpenAPI 문서에 정의된 같은 HTTP API를 직접 호출합니다.더 읽을거리
Wixzel Voice 소개
Wixzel Voice는 자체 SIP 트렁크로 실제 전화를 걸고 받거나 웹·모바일 앱에서 사람들과 대화하는 AI 에이전트를 만들기 위한 음성 AI API입니다. API 키 하나와 선불 잔액 하나로 모든 음성 엔진을 사용하며, 실제 사용량에 따라 초 단위로 과금됩니다.
더 많은 가이드
Claude Code나 claude.ai의 Claude가 SIP 트렁크로 실제 전화를 걸고 대화 기록을 읽어 주는 상태. 전화를 걸기 전에는 항상 확인을 거칩니다.
Sarvam의 인도 언어 모델로 동작하며 SIP 트렁크로 전화를 받거나 거는 전화 에이전트 두 개(힌디어 하나, 말라얄람어 하나).
API 키는 서버에 두고, 전화번호 없이 사용자가 WebSocket으로 음성 에이전트와 대화하는 Flutter 앱.
cURL 요청 네 번으로 SIP 트렁크를 통해 실제 전화를 거는 Gemini Live 에이전트, 그리고 대화 기록과 비용이 담긴 통화 기록.
에이전트가 SIP 트렁크로 리드 목록에 동시 통화 한도에 맞춘 배치 단위로 전화를 걸고, 모든 통화의 상태와 대화 기록을 읽어 오는 상태.
웹사이트 구석에서 방문자가 브라우저로 통화하는 말하는 마스코트. 지정한 도메인과 일일 한도 안에서만 동작하고, 모든 발신자가 목록에 남습니다.
실시간 통화 중 에이전트 음성에 맞춰 입을 움직이는 에이전트 마스코트가 있는 Flutter 화면. API 키는 서버에 둡니다.

