Voice APIGọi ra AI Streaming

Gọi ra AI Streaming

Lê Đức Tuệ·2/6/2026

Giao thức WebSocket để AI voice bot tích hợp với Voice Gateway. Vendor cung cấp WebSocket server theo spec dưới đây; Gateway sẽ kết nối đến khi cuộc gọi bắt đầu và trao đổi audio/control event trong suốt cuộc gọi. Mô hình tham chiếu: Twilio Media Streams.

Version 1.0 — Audience: AI Voice Bot Vendor

Mục lục

  1. Tổng quan & Kiến trúc

  2. Luồng xử lý — Sequence diagrams

  3. REST API — Khởi tạo cuộc gọi

  4. Kết nối WebSocket

  5. Định dạng Audio

  6. Events — Gateway → Bot

  7. Events — Bot → Gateway

  8. Liên hệ


01. Tổng quan & Kiến trúc

Tích hợp gồm 2 phần: REST API để vendor yêu cầu Gateway quay số, và WebSocket để Gateway stream audio/control event với bot sau khi caller nhấc máy.

Half-duplex ở v1: Bot phát xong audio response thì Gateway mới forward audio của caller lên. Bot không cần xử lý barge-in trong phiên bản này.

1.1 Kiến trúc tổng thể

          VENDOR SIDE                                    PLATFORM SIDE                   PSTN
┌──────────────────────────┐              ┌──────────────────────────────────┐         ┌────────┐
│                          │              │                                  │         │        │
│  ┌────────────────────┐  │   1. REST    │  ┌────────────────────────────┐  │   SIP   │        │
│  │                    │──┼──────────────┼─►│                            │─────────── ──►      │
│  │  REST client       │  │   POST       │  │     Voice Gateway          │  │         │ Caller │
│  │  (vendor app)      │◄─┼──────────────┼──│     (REST server)          │◄─────────────       │
│  │                    │  │   200 OK     │  │                            │  │         │        │
│  └────────────────────┘  │              │  └──────────────┬─────────────┘  │         │        │
│                          │              │                 │                │         │        │
│  ┌────────────────────┐  │              │                 │ 2. WS connect  │         │        │
│  │                    │◄─┼──────────────┼─────── WSS ─────┘ (sau khi caller│         │        │
│  │  AI Voice Bot      │  │              │                   nhấc máy)     │         │        │
│  │  (WebSocket        │──┼──────────────┼─────── WSS ─────────────────────►│         │        │
│  │   server)          │  │              │                                  │         └────────┘
│  │                    │  │              │                                  │
│  │  STT → LLM → TTS   │  │              │                                  │
│  └────────────────────┘  │              │                                  │
│                          │              │                                  │
└──────────────────────────┘              └──────────────────────────────────┘

Thành phần

Vai trò

Voice Gateway

WebSocket client (tới bot), đồng thời là REST server (nhận lệnh quay số từ vendor)

AI Voice Bot

WebSocket server (nhận kết nối từ Gateway), đồng thời là REST client (gọi API quay số)

1.2 Tóm tắt luồng

Bước

Hành động

Mô tả

1

Vendor yêu cầu quay số qua REST API

Gọi POST /v1/voice/callbot kèm phone, campaignId, transactionId, socketUrl, metadata cá thể hoá. API async — trả ngay 200 OK.

2

Gateway quay số qua SIP/PSTN

Gateway thực hiện outbound call tới caller. Nếu caller không nhấc máy → không có WebSocket nào được mở.

3

Gateway mở WebSocket tới bot

Sau khi caller nhấc máy, Gateway connect tới socketUrl của vendor, gửi connected rồi start với metadata cuộc gọi.

4

Trao đổi audio/control

Bot gửi audio response (media) + mark. Gateway phát audio cho caller, khi phát xong echo mark lại. Bot bật ASR, Gateway forward audio caller (media track inbound).

5

Kết thúc cuộc gọi

Caller cúp máy / bot gửi stop / bot transfer sang agent. Gateway gửi stop + đóng WS với close code 1000.


02. Luồng xử lý — Sequence diagrams

Ba scenario thường gặp: happy path có transfer, bot chủ động kết thúc, caller không nhấc máy.

2.1 Happy path — từ quay số đến kết thúc cuộc gọi


    AI Voice Bot                                 Voice Gateway                                   Caller
    ════════════                                 ═════════════                                   ══════
          │                                            │                                            │
════  PHASE 0 · Vendor yêu cầu quay số (REST API)  ═══════════════════════════════════════════════════════════
          │                                            │                                            │
          │   POST /v1/voice/callbot                   │                                            │
          ├───────────────────────────────────────────►│                                            │
          │phone, campaignId, transactionId, socketUrl │                                            │
          │                                            │                                            │
          │   200 OK  {error_code: "success"}          │                                            │
          │◄───────────────────────────────────────────┤                                            │
          │                                            │                                            │
          │                                            │   Dial (SIP / PSTN)                        │
          │                                            ├───────────────────────────────────────────►│
          │                                            │   Nhấc máy                                 │
          │                                            │◄───────────────────────────────────────────┤
          │                                            │                                            │
════  PHASE 1 · WebSocket handshake & start  ═════════════════════════════════════════════════════════════════
          │                                            │                                            │
          │   WS connect                               │                                            │
          │◄───────────────────────────────────────────┤                                            │
          │      wss://.../ws/voice?api_key=...        │                                            │
          │                                            │                                            │
          │   101 Switching Protocols                  │                                            │
          ├───────────────────────────────────────────►│                                            │
          │                                            │                                            │
          │   connected                                │                                            │
          │◄───────────────────────────────────────────┤                                            │
          │                                            │                                            │
          │   start                                    │                                            │
          │◄───────────────────────────────────────────┤                                            │
          │         call_sid, metadata.custom          │                                            │
          │                                            │                                            │
════  PHASE 2 · Loop hội thoại (lặp cho mỗi lượt)  ═══════════════════════════════════════════════════════════
          │                                            │                                            │
            ╭── [ BOT NÓI ] ──────────────────────────────────────────────────────────────────────╮
          │                                            │                                            │
          │   media                                    │                                            │
          ├───────────────────────────────────────────►│                                            │
          │          audio chunks (PCM 8kHz)           │                                            │
          │                                            │                                            │
          │                                            │   phát audio                               │
          │                                            ├┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈►│
          │                                            │                                            │
          │   mark  name: "turn_N_done"                │                                            │
          ├───────────────────────────────────────────►│                                            │
          │                                    [ chờ phát xong ]                                    │
          │   mark (echo)                              │                                            │
          │◄───────────────────────────────────────────┤                                            │
            ╰─────────────────────────────────────────────────────────────────────────────────────╯
          │                                            │                                            │
            ╭── [ CALLER NÓI ] ───────────────────────────────────────────────────────────────────╮
          │                                            │                                            │
          │                                            │   caller speak                             │
          │                                            │◄┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┤
          │                                            │                                            │
          │   media  track: "inbound"                  │                                            │
          │◄───────────────────────────────────────────┤                                            │
            ╰─────────────────────────────────────────────────────────────────────────────────────╯
          │                                            │                                            │
          │                         … bot xử lý và quay lại pha "Bot nói" …                         │
          │                                            │                                            │
════  PHASE 3 · Chuyển cuộc gọi sang agent  ══════════════════════════════════════════════════════════════════
          │                                            │                                            │
          │   transfer  target: "agent_01"             │                                            │
          ├───────────────────────────────────────────►│                                            │
          │                                            │                                            │
          │                                            │   SIP REFER / bridge → agent               │
          │                                            ├───────────────────────────────────────────►│
          │                                            │                                            │
          │   stop  reason: "transferred"              │                                            │
          │◄───────────────────────────────────────────┤                                            │
          │                                            │                                            │

Về mark: Bot gửi mark sau mỗi đoạn audio, Gateway echo lại mark khi caller đã nghe xong. Bot dùng tín hiệu này để biết khi nào nên bật ASR xử lý phản hồi của caller.

2.2 Bot chủ động kết thúc cuộc gọi


    AI Voice Bot                                 Voice Gateway
    ════════════                                 ═════════════
          │                                            │
════  Bot chủ động kết thúc cuộc gọi  ════════════════════════════════════════════════════════════════════════
          │                                            │
          │   media  (chunk cuối)                      │
          ├───────────────────────────────────────────►│
          │                                            │
          │   stop  reason: "conversation_complete"    │
          ├───────────────────────────────────────────►│
          │                                            │
          │             [ drain buffer ]               │
          │         phát nốt audio cho caller          │
          │                                            │
          │   stop (ack)  close code 1000              │
          │◄───────────────────────────────────────────┤
          │                                            │

2.3 Caller không nhấc máy


    AI Voice Bot                                 Voice Gateway                                   Caller
    ════════════                                 ═════════════                                   ══════
          │                                            │                                            │
          │   POST /v1/voice/callbot                   │                                            │
          ├───────────────────────────────────────────►│                                            │
          │                                            │                                            │
          │   200 OK  {error_code: "success"}          │                                            │
          │◄───────────────────────────────────────────┤                                            │
          │                                            │                                            │
          │                                            │   Dial (SIP / PSTN)                        │
          │                                            ├───────────────────────────────────────────►│
          │                                            │                                            │
                    ╔════════════════════════════════════════════════════════════════════╗
                    ║                                                                    ║
                    ║            Không nhấc máy  ·  máy bận  ·  thuê bao tắt             ║
                    ║                                                                    ║
                    ╚════════════════════════════════════════════════════════════════════╝

                                       → Không có WebSocket nào được mở

Lưu ý về phản hồi kết quả quay số: Ở v1, không có webhook trả kết quả dial cho vendor. Nếu caller không nhấc máy, vendor sẽ không nhận được thông tin — nên dùng transactionId để đối soát, hoặc liên hệ team tích hợp để lấy call_sid khi cần debug.


03. REST API — Khởi tạo cuộc gọi

Vendor gọi API này khi muốn Gateway quay số ra cho caller (ví dụ: trong campaign outbound). API là async — trả về ngay khi nhận yêu cầu; quá trình quay số và kết nối WebSocket diễn ra sau.

3.1 Endpoint

POST {{baseUrl}}/v1/voice/callbot

3.2 Xác thực

Header X-Api-Key được cấp khi vendor đăng ký bot.

X-Api-Key: <api_key>
Content-Type: application/json

3.3 Request

curl --location '{{baseUrl}}/v1/voice/callbot' \
  --header 'X-Api-Key: {{api_key}}' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "phone": "0123456789",
    "campaignId": 1,
    "transactionId": "TXN_004",
    "socketUrl": "wss://bot.vendor.com/ws/voice?api_key=xxx",
    "name": "John Doe",
    "email": "[email protected]",
    "address": "123 Main St, Anytown, USA",
    "pField1": "Thông tin cá thể hoá 1",
    "pField2": "Thông tin cá thể hoá 2",
    "pField3": "Thông tin cá thể hoá 3",
    "pField4": "Thông tin cá thể hoá 4",
    "pField5": "Thông tin cá thể hoá 5",
    "pField6": "Thông tin cá thể hoá 6"
  }'
const axios = require('axios')

const response = await axios.post(
  '{{baseUrl}}/v1/voice/callbot',
  {
    phone: '0123456789',
    campaignId: 1,
    transactionId: 'TXN_004',
    socketUrl: 'wss://bot.vendor.com/ws/voice?api_key=xxx',
    name: 'John Doe',
    email: '[email protected]',
    address: '123 Main St, Anytown, USA',
    pField1: 'Thông tin cá thể hoá 1',
    pField2: 'Thông tin cá thể hoá 2',
    pField3: 'Thông tin cá thể hoá 3',
    pField4: 'Thông tin cá thể hoá 4',
    pField5: 'Thông tin cá thể hoá 5',
    pField6: 'Thông tin cá thể hoá 6'
  },
  {
    headers: {
      'X-Api-Key': '{{api_key}}',
      'Content-Type': 'application/json'
    }
  }
)

console.log(response.data)
import requests

response = requests.post(
  '{{baseUrl}}/v1/voice/callbot',
  json={
    'phone': '0123456789',
    'campaignId': 1,
    'transactionId': 'TXN_004',
    'socketUrl': 'wss://bot.vendor.com/ws/voice?api_key=xxx',
    'name': 'John Doe',
    'email': '[email protected]',
    'address': '123 Main St, Anytown, USA',
    'pField1': 'Thông tin cá thể hoá 1',
    'pField2': 'Thông tin cá thể hoá 2',
    'pField3': 'Thông tin cá thể hoá 3',
    'pField4': 'Thông tin cá thể hoá 4',
    'pField5': 'Thông tin cá thể hoá 5',
    'pField6': 'Thông tin cá thể hoá 6'
  },
  headers={
    'X-Api-Key': '{{api_key}}',
    'Content-Type': 'application/json'
  }
)

print(response.json())

Request fields

Field

Type

Bắt buộc

Mô tả

phone

string

Số điện thoại caller

campaignId

int

ID campaign đã cấu hình trên Gateway

transactionId

string

ID giao dịch phía vendor, dùng để đối soát

socketUrl

string

URL WebSocket của bot — Gateway sẽ connect tới sau khi caller nhấc máy

name

string

Không

Tên khách hàng

email

string

Không

Email

address

string

Không

Địa chỉ

pField1pField6

string

Không

Các trường cá thể hoá, sẽ được forward vào metadata.custom khi Gateway mở WebSocket tới bot

3.4 Response

{
  "error_code": "success",
  "message": "OK"
}

Response semantics: HTTP 200 + error_code = "success" → Gateway đã nhận yêu cầu và sẽ quay số. Các giá trị error_code khác đều là lỗi (sai api_key, thiếu field, campaign không tồn tại, …).


04. Kết nối WebSocket

Gateway chủ động mở WebSocket tới socketUrl mà vendor đã cung cấp ở bước khởi tạo cuộc gọi.

4.1 URL & Xác thực

Gateway sẽ connect tới socketUrl mà vendor đã cung cấp trong API request. URL phải kèm api_key qua query param:

wss://bot.vendor.com/ws/voice?api_key=<KEY>

Bot verify api_key trong handshake: Nếu sai → đóng WebSocket với close code 1008 (policy violation).

4.2 Yêu cầu kỹ thuật

Yêu cầu

Giá trị

Protocol

WebSocket (RFC 6455)

Scheme

wss:// — TLS bắt buộc với production

Message format

JSON, text frames, UTF-8

Audio transport

Base64 trong field media.payload

4.3 Timeouts

Timeout

Giá trị mặc định

Connect timeout

5 giây

Idle timeout (không có media)

30 giây

Max session duration

900 giây

Kết thúc cuộc gọi: Khi cuộc gọi kết thúc, Gateway gửi event stop rồi đóng WS với close code 1000 (normal). Vendor không cần reconnect.


05. Định dạng Audio

Audio format cố định ở v1. Gateway và bot đều phải dùng đúng format này.

Thuộc tính

Giá trị

Codec

PCM signed 16-bit little-endian (pcm_s16le)

Sample rate

8000 Hz

Channels

1 (mono)

Frame size

20ms / chunk (160 samples = 320 bytes)

Transport

Base64 string trong JSON


06. Events — Gateway → Bot

Mọi message là JSON UTF-8 gửi qua WebSocket text frame.

6.1 connected

GATEWAY → BOT — Gửi ngay sau khi WebSocket handshake thành công.

{
  "event": "connected",
  "protocol": "voice_stream",
  "version": "1.0"
}

6.2 start

GATEWAY → BOT — Gửi 1 lần duy nhất sau connected. Chứa metadata cuộc gọi. Vendor nên cache thông tin này trong suốt session.

{
  "event": "start",
  "sequence_number": 1,
  "start": {
    "stream_sid": "MZxxxxxxxxxxxxxxxxx",
    "call_sid": "call-abc123",
    "media_format": {
      "encoding": "pcm_s16le",
      "sample_rate": 8000,
      "channels": 1
    },
    "metadata": {
      "phone_number": "0900000000",
      "direction": "outbound",
      "custom": {
        "key1": "value1",
        "key2": "value2"
      }
    }
  }
}

Field

Type

Mô tả

stream_sid

string

ID duy nhất của WebSocket stream

call_sid

string

ID cuộc gọi, dùng để log / trace

media_format

object

Luôn là PCM 8kHz mono s16le ở v1

metadata.phone_number

string

Số điện thoại của caller

metadata.direction

string

outbound / inbound

metadata.custom

object

Dynamic fields tuỳ cấu hình từng bot

metadata.custom: Các trường động do vendor và Gateway thoả thuận trước khi tích hợp. Schema được định nghĩa khi đăng ký bot (ví dụ: mapping từ pField1…pField6 ở REST API).

6.3 media

GATEWAY → BOT — Audio từ caller stream lên bot. Gửi liên tục ~20ms / chunk.

{
  "event": "media",
  "sequence_number": 42,
  "media": {
    "track": "inbound",
    "chunk": 41,
    "timestamp": 1776326027630,
    "payload": "<base64_pcm_data>"
  }
}

Field

Type

Mô tả

track

string

Luôn "inbound"

chunk

int

Số thứ tự chunk, bắt đầu từ 0

timestamp

int

Unix ms

payload

string

Base64 của raw PCM bytes (320 bytes / chunk)

6.4 mark

GATEWAY → BOT — Echo lại marker mà bot đã gửi. Gateway gửi mark lại cho bot khi audio tương ứng đã phát xong cho caller nghe.

{
  "event": "mark",
  "sequence_number": 80,
  "mark": {
    "name": "greeting_done"
  }
}

6.5 stop

GATEWAY → BOT — Cuộc gọi kết thúc. Gateway gửi rồi đóng WS.

{
  "event": "stop",
  "sequence_number": 999,
  "stop": {
    "reason": "caller_hangup",
    "call_sid": "call-abc123"
  }
}

reason

Mô tả

caller_hangup

Caller cúp máy

ai_hangup

Bot yêu cầu dừng

transferred

Cuộc gọi đã transfer thành công

timeout

Idle timeout

error

Lỗi hệ thống


07. Events — Bot → Gateway

Bot gửi audio response, marker, lệnh transfer hoặc stop về Gateway.

7.1 media

BOT → GATEWAY — Audio response bot phát cho caller nghe.

{
  "event": "media",
  "media": {
    "payload": "<base64_pcm_data>"
  }
}
  • Phải là PCM 8kHz mono s16le (đúng với start.media_format)

  • Chunk size nên 20–100ms để giảm latency

  • Không cần chunk / timestamp — Gateway tự sequence

7.2 mark

BOT → GATEWAY — Đặt checkpoint. Gateway sẽ echo lại khi audio trước nó đã phát xong cho caller.

{
  "event": "mark",
  "mark": {
    "name": "question_1_done"
  }
}

Bot dùng để biết khi nào caller đã nghe xong audio, từ đó bật ASR lắng nghe phản hồi.

7.3 transfer

BOT → GATEWAY — Chuyển cuộc gọi sang agent hoặc queue.

{
  "event": "transfer",
  "transfer": {
    "target": "agent_extension_or_queue",
    "context": "default",
    "on_complete": "hangup_bot"
  }
}

Field

Type

Mô tả

target

string

Extension / queue ID / phone number

context

string

Routing context, nhận từ Gateway khi đăng ký

on_complete

string

hangup_bot (đóng WS bot) / keep_alive (giữ WS)

Sau khi nhận transfer, Gateway sẽ:

  1. Phát nốt audio trong buffer (drain)

  2. Thực hiện transfer

  3. Gửi stop với reason transferred cho bot

  4. Đóng WS

7.4 stop

BOT → GATEWAY — Kết thúc cuộc gọi chủ động từ bot.

{
  "event": "stop",
  "stop": {
    "reason": "conversation_complete"
  }
}

Gateway sẽ phát nốt audio trong buffer rồi kết thúc cuộc gọi.


08. Liên hệ

Liên hệ team tích hợp Alohub khi cần hỗ trợ:

  • Review implementation: Sanity check spec và logic xử lý event trước khi lên production.

  • Cấu hình api_key & URL: Trên môi trường staging / production cho từng bot.

  • Debug cuộc gọi thật: Cần cung cấp call_sid hoặc transactionId để truy vết log.

Bài viết này có hữu ích không?
Cập nhật: 2/6/2026
để chuyển bài