Assistant CoreAssistant Core
Developer API

Voice API

Tạo voice session và quản lý audio qua WebSocket.

Voice API dùng hai bước: backend tạo session bằng Developer API key, sau đó client kết nối WebSocket bằng token ngắn hạn. Không gửi Developer API key tới browser.

Đây là advanced Preview path. Assistant Core chưa cung cấp browser voice SDK, WebRTC transport hoặc microphone/playback helper. Chỉ dùng khi đội của bạn có thể tự quản lý capture, resampling, codec, playback, cancel và reconnect.

1. Cho phép browser origin

Nếu client chạy trong browser, vào API cho nhà phát triển → Truy cập Voice API trên trình duyệt và thêm chính xác origin của website. Origin phải dùng http hoặc https; giao thức, hostname và cổng hiệu lực đều phải khớp.

Danh sách này độc lập với website được phép tải Web Widget. Server, mobile và desktop clients không gửi header Origin không cần bước này, nhưng vẫn phải dùng session token hợp lệ.

2. Tạo session từ backend

POST /v1/voice/sessions · scope voice:connect

curl "$ASSISTANT_API_URL/v1/voice/sessions" \
  --request POST \
  --header "Authorization: Bearer $ASSISTANT_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{
    \"external_user_id\": \"$EXTERNAL_USER_ID\",
    \"mode\": \"realtime\",
    \"turn_detection\": \"server_vad\",
    \"max_session_seconds\": 60,
    \"input_audio_format\": {
      \"codec\": \"pcm_s16le\",
      \"sample_rate_hz\": 16000,
      \"channels\": 1
    },
    \"output_audio_format\": {
      \"codec\": \"opus\",
      \"sample_rate_hz\": 24000,
      \"channels\": 1
    }
  }"
FieldÝ nghĩa
external_user_idIdentity 1–256 ký tự của người dùng cuối.
modestandard dùng ASR → LLM → TTS; realtime dùng speech-to-speech provider.
turn_detectionmanual (mặc định); auto cho standard mode; hoặc server_vad cho realtime mode.
max_session_secondsDuration do server enforce từ 10 đến 1800 giây; global maximum vẫn luôn áp dụng.
input_audio_formatpcm_s16le ở 16/24 kHz hoặc opus ở 24 kHz; mono.
output_audio_formatCùng lựa chọn codec; Opus bắt buộc 24 kHz; mono.
conversation_idOptional UUID để tiếp tục hội thoại của cùng external user.
include_metricsOptional, mặc định false. Khi true, server gửi response.metrics sau mỗi response hoàn tất.

Response 201:

{
  "id": "sess_01K...",
  "token": "short_lived_single_use_token",
  "expires_at": "2026-08-13T08:32:00Z",
  "websocket_url": "wss://api.assistantcore.com/v1/voice"
}

Chỉ trả id, token, expires_at và websocket_url cho client đã xác thực. Không trả Developer API key. Token hết hạn sau khoảng 120 giây và chỉ dùng một lần.

Mobile và desktop app

Không nhúng Developer API key vào APK, IPA hoặc desktop binary. App native là public client và secret đóng gói cùng ứng dụng có thể bị trích xuất.

Backend của bạn giữ Developer API key, xác thực người dùng app, tạo voice session rồi chỉ chuyển token, expires_at và websocket_url cho app. Lưu refresh token hoặc private key riêng của installation trong Keychain/Keystore nếu ứng dụng có cơ chế đăng nhập riêng; không dùng một Developer API key chung làm credential của app.

3. Mở WebSocket

Kết nối đúng websocket_url được trả về. Event đầu tiên trong vòng 15 giây phải là:

{
  "type": "session.start",
  "token": "short_lived_single_use_token"
}

Không đặt token trong URL. Server trả session.created khi session sẵn sàng:

{
  "type": "session.created",
  "sequence": 1,
  "session_id": "sess_01K..."
}

Mọi JSON server event có sequence tăng dần. Binary server messages là audio theo output format đã negotiate.

Browser connection skeleton dưới đây chỉ xử lý session và control events; phần capture/playback phụ thuộc audio stack của ứng dụng:

const session = await fetch("/api/assistant/voice-session", {
  method: "POST",
}).then((response) => response.json())

const socket = new WebSocket(session.websocket_url)
socket.binaryType = "arraybuffer"

socket.addEventListener("open", () => {
  socket.send(JSON.stringify({
    type: "session.start",
    token: session.token,
  }))
})

socket.addEventListener("message", (message) => {
  if (typeof message.data === "string") {
    const event = JSON.parse(message.data)
    console.log(event.type, event.sequence)
    return
  }

  // Queue message.data for playback using the negotiated output format.
})

socket.addEventListener("close", () => {
  // Discard this token. Ask the backend for a new session before reconnecting.
})

Gửi text

{ "type": "input_text.create", "text": "Chính sách đổi trả là gì?" }

Không gửi input mới khi response đang chạy. Gửi response.cancel trước nếu muốn barge-in.

Gửi audio

Với standard + manual hoặc realtime + manual, dùng thứ tự:

  1. Gửi {"type":"input_audio.start"}.
  2. Gửi một hoặc nhiều binary WebSocket messages theo input format đã chọn.
  3. Gửi {"type":"input_audio.commit"}.

Với standard + auto hoặc realtime + server_vad, gửi input_audio.start một lần sau session.created, stream binary audio liên tục và không gửi input_audio.commit. Server hoặc provider phát hiện speech boundary và tạo đúng một response cho mỗi lượt nói.

Với pcm_s16le, binary payload là raw signed 16-bit little-endian mono PCM, không có WAV header. Nên dùng frame ngắn đều đặn; mỗi binary message không được vượt 256 KiB. Với Opus, mỗi binary message chứa một Opus frame ở 24 kHz mono.

Client events

EventDùng khi
session.startEvent đầu tiên; gửi token.
input_audio.startMở manual turn hoặc continuous input stream cho server VAD.
Binary messageGửi audio sau input_audio.start.
input_audio.commitKết thúc manual turn; không hợp lệ khi bật server VAD.
input_text.createGửi text thay cho audio.
response.cancelHủy response đang chạy/barge-in.
pingKiểm tra kết nối; server trả pong.

Server events

EventNội dung
session.createdSession đã sẵn sàng. Ở Standard mode, nếu assistant có câu chào voice và session bắt đầu hội thoại mới, câu chào theo sau như một response bình thường (output_audio.*, rồi response.completed không có message_id). Người dùng nói hoặc response.cancel sẽ ngắt nó. Câu chào được lưu cùng lượt nói đầu tiên của người dùng.
conversation.createdID hội thoại mới.
input_audio.speech_startedServer đã mở audio input.
input_audio.speech_stoppedServer đã commit audio input.
transcript.completedTranscript hoàn tất ở standard mode.
response.text.deltaText delta của assistant.
output_audio.startedBinary output audio bắt đầu.
Binary messageAudio theo output format.
output_audio.stoppedBinary output audio đã kết thúc.
response.completedResponse hoàn tất hoặc đã cancel.
response.metricsPhân rã độ trễ của response vừa hoàn tất; chỉ có khi bật include_metrics.
error{code, message, retryable} nằm trong field error.
pongPhản hồi cho ping.

Latency metrics

Với include_metrics: true, mỗi response hoàn tất được theo sau bởi đúng một event response.metrics. Response bị cancel hoặc lỗi không gửi event này.

{
  "type": "response.metrics",
  "sequence": 42,
  "message_id": "7c1e...",
  "end_of_utterance_ms": 610,
  "transcription_ms": 180,
  "llm_ttft_ms": 420,
  "tts_ttfb_ms": 150,
  "e2e_latency_ms": 1390
}
FieldĐo từ → đến
end_of_utterance_msNgười dùng ngừng nói → server commit lượt nói (khoảng im lặng của VAD).
transcription_msCommit lượt nói → transcript cuối cùng.
llm_ttft_msTranscript cuối cùng → token LLM đầu tiên.
tts_ttfb_msRequest TTS đầu tiên → audio TTS đầu tiên.
e2e_latency_msNgười dùng ngừng nói → audio phản hồi đầu tiên được gửi.

Mọi giá trị là mili giây (số nguyên) đo tại server, không gồm thời gian mạng tới client. Field bị bỏ khi giai đoạn đó không đo được. Realtime mode chạy nhận dạng, suy luận và tổng hợp giọng bên trong provider nên chỉ trả e2e_latency_ms.

Cancel và barge-in

{ "type": "response.cancel" }

Sau khi gửi cancel, dừng playback/bỏ audio đang buffer ở client và chờ response.completed với status cancelled. Sau đó mới gửi input mới.

Reconnect

Session token không thể dùng lại. Khi kết nối mất:

  1. Đóng client socket và bỏ token cũ.
  2. Backend tạo session mới.
  3. Truyền conversation_id trước đó trong session request nếu muốn tiếp tục hội thoại.
  4. Kết nối tới URL mới và gửi token mới.

Không tự động retry vô hạn. Dùng exponential backoff cho lỗi retryable và dừng khi credential, origin, plan hoặc credits không hợp lệ.

Giới hạn kết nối hiện tại

  • JSON event tối đa 16 KiB; binary frame tối đa 256 KiB.
  • Tối đa 50 events/giây và 10 MiB/phút cho mỗi connection.
  • Tối đa 15 phút input audio, 30 phút/session và 2 phút idle. max_session_seconds nhỏ hơn sẽ được enforce khi nằm trong session token.
  • Phiên Realtime còn bị giới hạn theo số credits của owner: max_session_seconds được cấp không vượt ngân sách này, và phiên kết thúc vì hết ngân sách sẽ đóng với INSUFFICIENT_CREDITS thay vì SESSION_DURATION_EXCEEDED.
  • Tối đa 5 connections/credential và 50 connections/assistant.

Các giới hạn có thể thay đổi trong giai đoạn Preview. Xem Lỗi và giới hạn để xử lý close codes.

On this page