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_id | Identity 1–256 ký tự của người dùng cuối. |
mode | standard dùng ASR → LLM → TTS; realtime dùng speech-to-speech provider. |
turn_detection | manual (mặc định); auto cho standard mode; hoặc server_vad cho realtime mode. |
max_session_seconds | Duration do server enforce từ 10 đến 1800 giây; global maximum vẫn luôn áp dụng. |
input_audio_format | pcm_s16le ở 16/24 kHz hoặc opus ở 24 kHz; mono. |
output_audio_format | Cùng lựa chọn codec; Opus bắt buộc 24 kHz; mono. |
conversation_id | Optional UUID để tiếp tục hội thoại của cùng external user. |
include_metrics | Optional, 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ự:
- Gửi
{"type":"input_audio.start"}. - Gửi một hoặc nhiều binary WebSocket messages theo input format đã chọn.
- 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
| Event | Dùng khi |
|---|---|
session.start | Event đầu tiên; gửi token. |
input_audio.start | Mở manual turn hoặc continuous input stream cho server VAD. |
| Binary message | Gửi audio sau input_audio.start. |
input_audio.commit | Kết thúc manual turn; không hợp lệ khi bật server VAD. |
input_text.create | Gửi text thay cho audio. |
response.cancel | Hủy response đang chạy/barge-in. |
ping | Kiểm tra kết nối; server trả pong. |
Server events
| Event | Nội dung |
|---|---|
session.created | Session đã 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.created | ID hội thoại mới. |
input_audio.speech_started | Server đã mở audio input. |
input_audio.speech_stopped | Server đã commit audio input. |
transcript.completed | Transcript hoàn tất ở standard mode. |
response.text.delta | Text delta của assistant. |
output_audio.started | Binary output audio bắt đầu. |
| Binary message | Audio theo output format. |
output_audio.stopped | Binary output audio đã kết thúc. |
response.completed | Response hoàn tất hoặc đã cancel. |
response.metrics | Phâ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. |
pong | Phả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_ms | Người dùng ngừng nói → server commit lượt nói (khoảng im lặng của VAD). |
transcription_ms | Commit lượt nói → transcript cuối cùng. |
llm_ttft_ms | Transcript cuối cùng → token LLM đầu tiên. |
tts_ttfb_ms | Request TTS đầu tiên → audio TTS đầu tiên. |
e2e_latency_ms | Ngườ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:
- Đóng client socket và bỏ token cũ.
- Backend tạo session mới.
- Truyền
conversation_idtrước đó trong session request nếu muốn tiếp tục hội thoại. - 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_secondsnhỏ 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ớiINSUFFICIENT_CREDITSthay 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.