REST API
Note: The URL used in this document (
vas-poc.vurbo.ai) is the planned deployment URL. A separate notice will be provided once the service goes live.
Table of Contents
- Table of Contents
- API Overview
- GET /api/v1/version (Look Up the Deployed Version)
- GET /api/v1/me/plan (Look Up My Plan)
- GET /api/v1/me/credit-lots, /me/usage, /me/key (API Key Self-Service)
- GET /api/v1/tasks (List Tasks)
- DELETE /api/v1/tasks/{taskId} (Delete Task)
- PUT /api/v1/tasks/batch/pin (Batch Update Pin Status)
- DELETE /api/v1/tasks/batch (Batch Delete Tasks)
- PUT /api/v1/tasks/{taskId}/pin (Update Pin Status)
- PUT /api/v1/tasks/{taskId}/read (Mark as Read)
- PATCH /api/v1/tasks/{taskId}/name (Update Task Name)
- GET /api/v1/tasks/{taskId}/audio/export (Download Task Audio)
- GET /api/v1/tasks/{taskId}/transcript/export (Download Transcript)
- POST /api/v1/tasks/{taskId}/force-fail (Force Mark as Failed)
- POST /api/v1/tasks/{taskId}/retry (Reprocess Failed Task)
- Audio Import API
- Audio API
- TTS API
- Broadcasts API
- Viewer API
- Recording Speaker Editing API
- Summary Template API
- Glossary Validation API
- Error Handling
API Overview
| Item | Value |
|---|---|
| Base Path | https://vas-poc.vurbo.ai/api/v1 |
| Protocol | HTTPS |
| Data Format | JSON |
Authentication
APIs that require authentication pass the API Key via an HTTP header:
X-API-Key: vas_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
API Categories
| Category | Path Prefix | Authentication | Purpose |
|---|---|---|---|
| Tasks API | /api/v1/tasks | Header X-API-Key | Task management, audio/transcript export |
| Import API | /api/v1/imports | Header X-API-Key | Audio import |
| Audio API | /api/v1/sse/audio | Header X-API-Key | Audio file playback |
| TTS API | /api/v1/tts | Header X-API-Key | TTS voice service |
| Broadcasts API | /api/v1/broadcasts | Header X-API-Key | Broadcast management |
| Viewer API | /api/v1/viewer/broadcasts | None | Viewer-side public info |
| Recording Speaker API | /api/v1/tasks/{taskId}/speakers | Header X-API-Key | Transcript speaker editing (since V1.4.1 the recordings path is a deprecated alias, removed in V1.6.0) |
| Summary Template API | /api/v1/summary-templates | Header X-API-Key | Summary template lookup |
| Broadcast REST API | /broadcast | Token (path parameter) | Broadcast real-time status |
| Version API | /api/v1/version | None | Deployed version lookup (for pre-release version gates) |
| My Plan API | /api/v1/me/plan | Header X-API-Key | Look up the current billing mode, plan contents, and usage (v1.9.0) |
| Key Self-Service API | /api/v1/me/credit-lots, /api/v1/me/usage, /api/v1/me/key | Header X-API-Key | Look up this API Key's credit lots, usage history, and settings (V1.21.0) |
| Glossary Validation API | /api/v1/glossary/validate | Header X-API-Key | Check a glossary for conflicts before saving (V1.14.0, served by the realtime service domain) |
GET /api/v1/version (Look Up the Deployed Version)
Description
Reports the version currently deployed, so integrators can run a version gate before going live — for example, "this fix requires ≥ vX.Y.Z".
No authentication required, consistent with health-check endpoints. A version gate that fails on authentication would be indistinguishable from a version mismatch, defeating its purpose.
Note: The realtime service and the REST service are versioned independently and may report different versions. If the fix you need to verify belongs to live recording or broadcasting (WebSocket), query the realtime service's
/versioninstead (see below).
Request Example
curl -X GET "https://vas-poc.vurbo.ai/api/v1/version"
Success Response (HTTP 200)
{
"service": "vas-api",
"version": "1.7.7",
"build": "a1b2c3d4e5f6"
}
Response Fields
| Field | Type | Description |
|---|---|---|
service | string | Service identifier: vas-api (REST) or vas-realtime (WebSocket) |
version | string | Platform version, matching this documentation's version (e.g. 1.7.7) |
build | string | Build identifier for tracing a specific build; one version may have several builds |
Realtime (WebSocket) Service Version
The realtime service exposes the same endpoint at /version.
Use the same host you connect to over WebSocket, swapping the wss:// scheme for https://. (In some environments the realtime and REST services live on different hosts — use the connection settings you were issued.)
# If your WebSocket connection is wss://<realtime-host>/ws
curl -X GET "https://<realtime-host>/version"
{
"service": "vas-realtime",
"version": "1.7.7",
"build": "a1b2c3d4e5f6"
}
Recommended Version-Gate Logic
This endpoint is available from V1.7.7 onward, which means:
- The endpoint responds → the version is necessarily ≥ V1.7.7; compare
versiondirectly. - The endpoint returns 404 → the version predates V1.7.7 and cannot be determined here; please confirm with your service contact.
GET /api/v1/me/plan (Look Up My Plan)
Description
Look up the billing mode and plan contents this API Key currently operates under (added in v1.9.0). When blocked by a plan limit such as plan_feature_not_allowed (HTTP 403) or plan_daily_limit_reached (HTTP 402), use this endpoint to answer "what does my plan include, how far am I from a limit, and when does the restriction lift?"
Read-only endpoint: still available after credits run out.
The full request/response specification (complete field tables for the three response shapes) is in reference/rest/me-plan.md.
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Example
curl -X GET "https://vas-poc.vurbo.ai/api/v1/me/plan" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
Success Response (HTTP 200)
The response shape depends on the billing mode:
Unlimited plan (mode: "unlimited", plan-bound):
{
"data": {
"mode": "unlimited",
"plan": {
"name": "專業方案",
"expired_at": "2027-07-31T23:59:59+08:00"
},
"features": [
{ "slug": "stt", "name": "基礎語音轉錄", "included": true },
{ "slug": "diarization", "name": "語者分離", "included": true },
{ "slug": "broadcast", "name": "廣播", "included": false }
],
"oneoff_features": [
{ "slug": "summary", "name": "AI 會議摘要", "included": true },
{ "slug": "import", "name": "檔案匯入", "included": false }
],
"limits": {
"daily_soft_limit_minutes": 480,
"daily_hard_limit_minutes": 600,
"max_concurrent_sessions": 2,
"daily_used_minutes": 123,
"max_transcription_languages": 4,
"max_session_minutes": 240,
"rolling_limit_minutes": 3000,
"rolling_used_minutes": 850,
"auth_total_limit_minutes": 60000,
"auth_total_used_minutes": 12345,
"restriction_recovery_at": null
}
}
}
Credit mode (mode: "credit"):
{
"data": {
"mode": "credit",
"plan": null,
"available_credit": 480.5
}
}
Unlimited authorization with no plan restrictions (mode: "unlimited", rare):
{
"data": {
"mode": "unlimited",
"plan": null,
"features": { "all": true },
"expired_at": "2027-01-31T23:59:59+08:00"
}
}
Main fields: features[] lists per-minute billed features (included indicates whether the plan includes them; broadcasting is always false), oneoff_features[] lists one-off features (summary, full-text re-translation, audio import), and limits holds the plan's limits and current usage (null = unrestricted). The full field reference is in reference/rest/me-plan.md.
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
auth_missing_api_key | 401 | API Key not provided | Make sure the header includes the API Key |
auth_invalid_api_key | 401 | Invalid API Key | Verify that the API Key is correct |
GET /api/v1/me/credit-lots, /me/usage, /me/key (API Key Self-Service)
Description
Use an API Key to look up its own credit lots, usage history, and settings (added in V1.21.0). All three are read-only endpoints, queryable even with zero balance, and return only this API Key's own data.
| Endpoint | Purpose |
|---|---|
GET /api/v1/me/credit-lots | Credit lots drawn on when charging (the dedicated allotment and, when allowed, account credit); only lots with credit left that have not expired, soonest expiry first |
GET /api/v1/me/usage | Charge history, one row per recording / broadcast and per import / summary / retranslation, newest first; supports page and per_page (5 to 20, default 20) |
GET /api/v1/me/key | The API Key's name, expiry date, monthly credit limit and this month's spend, concurrency limit, webhook URL, and whether a source IP restriction is configured |
The full request/response specification is in reference/rest/me-key.md.
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Example
curl -X GET "https://vas-poc.vurbo.ai/api/v1/me/usage?page=1&per_page=20" \
-H "X-API-Key: YOUR_API_KEY"
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
validation_failed | 422 | Invalid page or per_page on /me/usage | Use page ≥ 1 and an integer per_page from 5 to 20 |
auth_missing_api_key | 401 | API Key not provided | Make sure the header includes the API Key |
auth_invalid_api_key | 401 | Invalid API Key | Verify that the API Key is correct |
GET /api/v1/tasks (List Tasks)
Description
Retrieve the recording task list for the current user. You can use the status parameter to filter tasks by processing stage, or task_ids[] to query specific tasks only.
Use Cases
- Display the task history list
- View completed recordings
- Query in-progress recording tasks
- Check the current processing status of specific tasks
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters (Query)
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
status | string | No | completed | Filter task status: completed, active, all |
task_ids[] | string[] | No | — | Query these tasks only (each element is a UUID, 1–100 entries) |
| status Value | Description |
|---|---|
completed | Return only completed tasks (default, backward compatible) |
active | Return in-progress tasks (recording, importing, uploading, processing) |
all | Return all tasks without filtering by status |
task_ids Filter Details
- Returns only the listed tasks that belong to the current account. IDs that do not exist or do not belong to the current account are skipped without an error.
statusstill applies: withoutstatus, only completed tasks are returned. To look up the listed tasks regardless of status, addstatus=all.- The response format is the same as without
task_ids. - More than 100 entries, an entry that is not a UUID, or
task_idsthat is not an array (for example,task_ids=<UUID>without[]) returns 422validation_failed.
Request Example
# Default query (completed tasks)
curl -X GET "https://vas-poc.vurbo.ai/api/v1/tasks" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
# Query in-progress tasks
curl -X GET "https://vas-poc.vurbo.ai/api/v1/tasks?status=active" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
# Query specific tasks only (regardless of status, add status=all)
curl -G "https://vas-poc.vurbo.ai/api/v1/tasks" \
--data-urlencode "task_ids[]=550e8400-e29b-41d4-a716-446655440000" \
--data-urlencode "task_ids[]=6ba7b810-9dad-11d1-80b4-00c04fd430c8" \
--data-urlencode "status=all" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
Success Response
| Field | Type | Description |
|---|---|---|
data.tasks | array | Task list |
data.tasks[].task_id | string | Task ID (UUID) |
data.tasks[].title | string | Task title |
data.tasks[].type | string | Recording type |
data.tasks[].type_source | string | Source type (realtime / import / broadcast) |
data.tasks[].duration_ms | number | Recording duration (milliseconds) |
data.tasks[].duration_formatted | string | Formatted duration (min:sec) |
data.tasks[].transcription_languages | array | Transcription language list |
data.tasks[].translation_languages | array | Translation language list |
data.tasks[].created_at | string | Creation time (ISO 8601) |
data.tasks[].processing_status | string | Processing status |
data.tasks[].is_pinned | boolean | Whether pinned |
data.tasks[].is_unread | boolean | Whether unread |
processing_status Values
| Status | Description | Applicable Scenario |
|---|---|---|
recording | Recording in progress | Real-time recording, broadcast |
importing | Audio import in progress | Audio import |
uploading | Uploading to the cloud | Upload phase after recording stops |
processing | Post-processing | Summary, translation, etc. |
completed | Processing complete | All scenarios |
failed | Processing failed | All scenarios |
Response Example
{
"data": {
"tasks": [
{
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Meeting Notes",
"type": "transcribe",
"type_source": "realtime",
"duration_ms": 60000,
"duration_formatted": "1:00",
"transcription_languages": ["zh-TW"],
"translation_languages": ["en-US"],
"created_at": "2026-02-25T10:00:00Z",
"processing_status": "completed",
"is_pinned": false,
"is_unread": true
}
]
}
}
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
auth_missing_api_key | 401 | API Key not provided | Make sure the header includes the API Key |
auth_invalid_api_key | 401 | Invalid API Key | Verify that the API Key is correct |
validation_failed | 422 | Parameter validation failed | Verify that task_ids is a UUID array of 1–100 entries |
DELETE /api/v1/tasks/{taskId} (Delete Task)
Description
Delete the specified task. Deletion is permanent and cannot be undone: the task is removed together with its audio, transcript, summary, translations, and imported source file, and can no longer be queried, exported, or retranslated.
A task that is still being processed cannot be deleted (processing_status is recording, importing, uploading, pending, or processing, or the task's import is still being processed); the request returns 422 invalid_processing_status. Wait until it completes or fails before deleting it. If a task is stuck, you can first mark it as failed with POST /api/v1/tasks/{taskId}/force-fail. This does not help when the task's import is still being processed; wait until the import finishes, then delete the task.
Use Cases
- Clear recording records you no longer need
- Organize the task list
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
taskId | string | Yes | Task ID (UUID) |
Request Example
curl -X DELETE "https://vas-poc.vurbo.ai/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
Success Response
{
"message": "Task deleted"
}
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
recording_not_found | 404 | The specified recording was not found | Verify that taskId is correct |
invalid_processing_status | 422 | The task is still being processed and cannot be deleted | Wait until the task completes or fails; for a stuck recording, use force-fail first. If the import is still being processed, wait until it finishes |
PUT /api/v1/tasks/batch/pin (Batch Update Pin Status)
Description
Batch update the pin status of multiple tasks. Up to 100 tasks can be operated on per request. Only tasks belonging to the current user are affected; IDs that do not belong to the user are ignored.
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
task_ids | body | array | Yes | Array of task IDs (each element a UUID, up to 100) |
is_pinned | body | boolean | Yes | Pin status |
Request Example
curl -X PUT "https://vas-poc.vurbo.ai/api/v1/tasks/batch/pin" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-H "Content-Type: application/json" \
-d '{
"task_ids": [
"550e8400-e29b-41d4-a716-446655440000",
"6ba7b810-9dad-11d1-80b4-00c04fd430c8"
],
"is_pinned": true
}'
Success Response
{
"data": {
"affected_count": 2
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
data.affected_count | number | Number of tasks actually updated |
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
validation_failed | 422 | Parameter validation failed | Make sure task_ids is an array of UUIDs with no more than 100 entries, and is_pinned is a boolean |
DELETE /api/v1/tasks/batch (Batch Delete Tasks)
Description
Batch delete multiple tasks. Deletion is permanent and cannot be undone, with the same scope as single-task deletion. Up to 100 tasks can be operated on per request. Only tasks belonging to the current user are affected; IDs that do not belong to the user are ignored.
Tasks that are still being processed are skipped and not deleted, and are listed in skipped_task_ids in the response. The remaining tasks are deleted as usual; the batch as a whole does not fail because of them.
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
task_ids | body | array | Yes | Array of task IDs (each element a UUID, up to 100) |
Request Example
curl -X DELETE "https://vas-poc.vurbo.ai/api/v1/tasks/batch" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-H "Content-Type: application/json" \
-d '{
"task_ids": [
"550e8400-e29b-41d4-a716-446655440000",
"6ba7b810-9dad-11d1-80b4-00c04fd430c8"
]
}'
Success Response
{
"data": {
"affected_count": 2,
"skipped_task_ids": []
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
data.affected_count | number | Number of tasks actually deleted |
data.skipped_task_ids | string[] | IDs of tasks that were not deleted because they are still being processed. Empty array when every requested task was deleted. IDs that do not belong to the current user never appear here |
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
validation_failed | 422 | Parameter validation failed | Make sure task_ids is an array of UUIDs with no more than 100 entries |
PUT /api/v1/tasks/{taskId}/pin (Update Pin Status)
Description
Update the pin status of a task. Pinned tasks are displayed first in the list.
Use Cases
- Mark important recordings
- Quickly access frequently used tasks
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
taskId | string | Yes | Task ID (path parameter) |
is_pinned | boolean | Yes | Pin status |
Request Example
curl -X PUT "https://vas-poc.vurbo.ai/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000/pin" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-H "Content-Type: application/json" \
-d '{"is_pinned": true}'
Success Response
{
"data": {
"is_pinned": true
}
}
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
recording_not_found | 404 | The specified recording was not found | Verify that taskId is correct |
validation_failed | 422 | Parameter validation failed | Make sure is_pinned is a boolean |
PUT /api/v1/tasks/{taskId}/read (Mark as Read)
Description
Mark a task as read.
Use Cases
- Mark recordings you have viewed
- Clear the unread indicator
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
taskId | string | Yes | Task ID (path parameter) |
Request Example
curl -X PUT "https://vas-poc.vurbo.ai/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000/read" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
Success Response
{
"data": {
"is_unread": false
}
}
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
recording_not_found | 404 | The specified recording was not found | Verify that taskId is correct |
PATCH /api/v1/tasks/{taskId}/name (Update Task Name)
Description
Update the name of the specified task.
Use Cases
- Customize the recording title
- Correct an automatically generated name
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
taskId | string | Yes | Task ID (path parameter) |
name | string | Yes | Task name (max 60 characters) |
Request Example
curl -X PATCH "https://vas-poc.vurbo.ai/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000/name" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-H "Content-Type: application/json" \
-d '{"name": "Product Meeting Discussion"}'
Success Response
{
"message": "Recording name updated",
"data": {
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Product Meeting Discussion",
"name_source": "user"
}
}
| Field | Type | Description |
|---|---|---|
name_source | string | Name source: default, llm, user |
Name Source Explanation:
| name_source | Description | Trigger Condition |
|---|---|---|
user | A name explicitly set by the user | set_name, this REST API (the system will not override it) |
llm | Automatically generated by the system from the transcript | At the end of a recording, if name_source is not user, the system generates one automatically |
default | Default name | The name passed to start (initial default, the system may still override it) or type + sequence number (e.g. Transcription #1) |
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
recording_not_found | 404 | The specified recording was not found | Verify that taskId is correct |
recording_unauthorized | 403 | Not authorized to operate on this recording | Confirm the task belongs to the user |
validation_failed | 422 | Validation failed | Make sure name is not empty and its length is valid |
GET /api/v1/tasks/{taskId}/audio/export (Download Task Audio)
Description
Download the original audio file of the specified task. The response is a binary audio stream with a Content-Disposition: attachment header, so a browser or download tool saves the content directly as a file. The file name uses the recording name first (a sanitized file name); if the name is empty, it falls back to audio.
Difference from the audio streaming API (
GET /api/v1/sse/audio/{taskId}):
- This endpoint: for offline download; the response includes a
Content-Disposition: attachmentheader; Range Requests are not supported.- Audio streaming: for playback; supports HTTP Range Requests for seeking/fast-forward; the response does not force a download.
Use Cases
- Save recording files offline
- Batch export the original audio of all tasks
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
taskId | path | string | Yes | Task ID (UUID) |
Request Example
curl -X GET "https://vas-poc.vurbo.ai/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000/audio/export" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-OJ
Tip:
curl -OJlets curl automatically name the saved file based on the server'sContent-Dispositionresponse.
Success Response
HTTP 200
HTTP/1.1 200 OK
Content-Type: audio/mp4
Content-Length: 1234567
Content-Disposition: attachment; filename="audio.m4a"; filename*=UTF-8''%E6%9C%83%E8%AD%B0%E8%A8%98%E9%8C%84.m4a
Cache-Control: no-cache
Note: All recording audio files are returned as an M4A container (AAC encoding).
Content-Typeis fixed toaudio/mp4, and the file extension is.m4a.
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
recording_not_found | 404 | The specified recording was not found, or the audio file does not exist in cloud storage | Verify that taskId is correct and the recording has not been deleted |
recording_audio_not_ready | 422 | The audio file has not finished uploading or is still processing | Retry later; first confirm via GET /api/v1/tasks that processing_status is completed |
storage_download_failed | 500 | Storage service download failed | Retry later; if it keeps failing, contact support |
GET /api/v1/tasks/{taskId}/transcript/export (Download Transcript)
Description
Download the transcript of the specified task, supporting five formats: plain text, SubRip subtitles, YouTube SBV subtitles, WebVTT subtitles, and CSV spreadsheet. The response includes the original text and all translation languages; the response includes a Content-Disposition: attachment header for direct download. The file name uses the recording name first (a sanitized file name); if the name is empty, it falls back to transcript, and a -transcript.{ext} suffix is always appended.
Difference from the transcript history SSE API (
GET /api/v1/sse/history/transcribe/{taskId}):
- This endpoint: for offline download; returns the complete file at once; can be opened directly in subtitle software or a spreadsheet.
- SSE history API: for progressive loading; pushes raw structured data sentence by sentence as an event stream (JSON fragments) for progressive rendering in the frontend UI.
Use Cases
- Download the transcript for subtitle software (SRT / SBV / VTT)
- Export CSV to open in Excel or data analysis tools
- Save the plain-text transcript offline
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Location | Type | Required | Default | Description |
|---|---|---|---|---|---|
taskId | path | string | Yes | — | Task ID (UUID) |
format | query | string | No | txt | Format: txt / srt / sbv / vtt / csv |
format Explanation
| Format | Time Format | Content Structure | Typical Use |
|---|---|---|---|
txt | — | One line per segment, [Speaker] original text; translations indented 4 spaces as [language code] translated text | Reading, record keeping |
srt | HH:MM:SS,mmm | Includes a sequence number; after the time axis of each segment, the original text and translation each occupy one line | SubRip subtitles (DaVinci Resolve, VLC, etc.) |
sbv | H:MM:SS.mmm | No sequence number; the time axis is separated by ,; the original text and translation are joined with | into a single line (line breaks are replaced with spaces) | YouTube subtitle upload |
vtt | HH:MM:SS.mmm | Uses WEBVTT as the header; after the time axis of each segment, the original text and translation each occupy one line | HTML5 <track> subtitles, web players |
csv | HH:MM:SS (no milliseconds) | Begins with a UTF-8 BOM; columns index,start,end,speaker,text,<one column per translation language> | Excel, data analysis |
Request Example
# Default TXT format
curl -X GET "https://vas-poc.vurbo.ai/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000/transcript/export" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-OJ
# Specify SRT format
curl -X GET "https://vas-poc.vurbo.ai/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000/transcript/export?format=srt" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-OJ
# CSV (Excel can open it directly; the UTF-8 BOM ensures non-ASCII text is not garbled)
curl -X GET "https://vas-poc.vurbo.ai/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000/transcript/export?format=csv" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-OJ
Success Response
HTTP 200
HTTP/1.1 200 OK
Content-Type: text/csv; charset=UTF-8
Content-Length: 2048
Content-Disposition: attachment; filename="transcript.csv"; filename*=UTF-8''%E6%9C%83%E8%AD%B0%E8%A8%98%E9%8C%84-transcript.csv
Cache-Control: no-cache
Note:
Content-Typeis determined dynamically based on theformatparameter:
Format Content-Type txttext/plain; charset=UTF-8srtapplication/x-subripsbvtext/plain; charset=UTF-8vtttext/vtt; charset=UTF-8csvtext/csv; charset=UTF-8
Output Example
Assume the transcript contains two segments of English audio (en-US) and two translations (zh-TW, ja-JP):
TXT
[Alice] Hello, good morning
[zh-TW] 你好,早安
[ja-JP] おはよう
[Bob] Thanks
[zh-TW] 多謝
[ja-JP] ありがとう
SRT
1
00:00:00,500 --> 00:00:03,000
Hello, good morning
你好,早安
おはよう
2
00:00:03,000 --> 00:00:04,200
Thanks
多謝
ありがとう
SBV
0:00:00.500,0:00:03.000
Hello, good morning | 你好,早安 | おはよう
0:00:03.000,0:00:04.200
Thanks | 多謝 | ありがとう
VTT
WEBVTT
00:00:00.500 --> 00:00:03.000
Hello, good morning
你好,早安
おはよう
00:00:03.000 --> 00:00:04.200
Thanks
多謝
ありがとう
CSV (the file begins with a UTF-8 BOM EF BB BF)
index,start,end,speaker,text,zh-TW,ja-JP
1,00:00:00,00:00:03,Alice,"Hello, good morning",你好,早安,おはよう
2,00:00:03,00:00:04,Bob,Thanks,多謝,ありがとう
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
recording_not_found | 404 | The specified recording was not found | Verify that taskId is correct and the recording has not been deleted |
recording_transcript_not_ready | 422 | The transcript has not finished generating or is empty | First confirm via GET /api/v1/tasks that processing_status = completed, then call again |
validation_failed | 422 | Parameter validation failed | Make sure format is one of the allowed values (txt / srt / sbv / vtt / csv) |
storage_download_failed | 500 | Storage service download failed | Retry later; if it keeps failing, contact support |
POST /api/v1/tasks/{taskId}/force-fail (Force Mark as Failed)
Description
Force a task that is stuck in a non-terminal state (recording / importing / uploading / pending / processing) to be marked as failed. After a successful operation, processing_status becomes failed, processing_error is set to the reason provided by the user, and a recording.failed webhook is triggered (payload.failure_source = user_forced). Tasks already in a terminal state (completed / failed) will receive a 422.
For the full specification and frontend example, see: Tasks API — POST force-fail.
Authentication
Header: X-API-Key.
Request Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
taskId | path | string | Yes | Task ID (UUID) |
reason | body | string | null | No | Failure reason, up to 500 characters |
Request Example
curl -X POST "https://vas-poc.vurbo.ai/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000/force-fail" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-H "Content-Type: application/json" \
-d '{"reason": "The recording client was disconnected too long; giving up waiting"}'
Success Response
HTTP 200
{
"data": {
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"processing_status": "failed",
"processing_error": "User-forced failure: The recording client was disconnected too long; giving up waiting (previous status: recording)"
}
}
Error Responses
| Error Code | HTTP | Description | Recommended Action |
|---|---|---|---|
recording_not_found | 404 | Recording not found or not owned by you | Verify that taskId is correct |
invalid_processing_status | 422 | The task is already in a terminal state | If completed, use DELETE; if failed, there is no need to force it again |
validation_failed | 422 | reason exceeds 500 characters or taskId is malformed | Check the length of reason and the UUID format |
POST /api/v1/tasks/{taskId}/retry (Reprocess Failed Task)
Description
Re-enqueue a task that is in the failed state for processing. After a successful operation, processing_status becomes processing, and processing_error is cleared. Re-queuing happens only after the status update has taken effect, so it never reads a stale status.
Preconditions: processing_status = failed and audio_status = success and transcript_status = success. If any one fails, a 422 is returned (the details include audio_status / transcript_status to help diagnose).
For the full specification, see: Tasks API — POST retry.
Authentication
Header: X-API-Key.
Request Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
taskId | path | string | Yes | Task ID (UUID) |
Request Example
curl -X POST "https://vas-poc.vurbo.ai/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000/retry" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
Success Response
HTTP 200
{
"data": {
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"processing_status": "processing"
}
}
Error Responses
| Error Code | HTTP | Description | details Field | Recommended Action |
|---|---|---|---|---|
recording_not_found | 404 | Recording not found or not owned by you | — | Verify that taskId is correct |
invalid_processing_status | 422 | The task is not in the failed state | current_status | Only failed tasks can be retried |
invalid_processing_status | 422 | The audio / transcript was not fully uploaded | current_status, audio_status, transcript_status | Confirm the source is complete; if corrupted, use force-fail instead |
task_already_processing | 409 | Another processing run for the same task has not finished yet | task_id | Send the same request again later; the task status was not changed |
Audio Import API
The audio import API lets you upload audio files for speech recognition and translation.
POST /api/v1/imports/check-quota (Check Credit)
Description
Check whether the user has enough credit to upload an audio file of the specified duration. We recommend calling this API for a pre-check before uploading.
Use Cases
- Check whether credit is sufficient before uploading an audio file
- Display the remaining available credit
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
duration_ms | integer | Yes | Audio duration (milliseconds; 1 second to 10 hours by default -- the actual bounds follow the deployment setting and match the duration check applied after upload) |
Request Example
curl -X POST "https://vas-poc.vurbo.ai/api/v1/imports/check-quota" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-H "Content-Type: application/json" \
-d '{"duration_ms": 3600000}'
Success Response
{
"data": {
"allowed": true,
"reason": null,
"is_unlimited": false,
"remain_quota": 480.0,
"duration_minutes": 60,
"estimated_points": 60.0
}
}
| Field | Type | Description |
|---|---|---|
allowed | boolean | Whether the upload is allowed (true when credit is sufficient or the plan allows it) |
reason | string | null | Why the upload is not allowed: null (allowed) / insufficient_credit (insufficient credit; topping up resolves it) / plan_not_allowed (the plan does not include audio import; a plan upgrade is required) / plan_daily_limit_reached (today's plan usage is exhausted and resets tomorrow; topping up does not help, added in v1.16.4) |
is_unlimited | boolean | Whether the account is unlimited (no point limit) |
remain_quota | float | null | Remaining credit points; null when unlimited. v1.9.0 semantics change: API Keys with a dedicated credit allotment receive the credit actually available to that key (its dedicated allotment, not the account's total balance); accounts without dedicated allotments see no change |
duration_minutes | integer | Estimated audio duration (minutes, rounded up) |
estimated_points | float | Estimated points charged (STT-based; actual also includes translation/diarization, finalized at upload) |
POST /api/v1/imports (Upload Audio)
Description
Upload an audio file for speech recognition and translation. After a successful upload, processing happens in the background, and you can track progress via the status query API.
Use Cases
- Upload a recording file for transcription
- Batch process audio files
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters (multipart/form-data)
| Parameter | Type | Required | Description |
|---|---|---|---|
file | file | Yes | Audio file (mp3/wav/m4a, max 500MB). The format is determined from the actual content; when the extension does not match the content, the file is processed according to its content |
transcription_languages | string | Yes | Transcription languages (JSON array, e.g. ["zh-TW"]) |
translation_languages | string | No | Translation languages (JSON array, e.g. ["en-US"]) |
recognition_mode | string | Yes | Recognition mode: single / multi_speaker. multi_language or multi_channel returns 422 import_recognition_mode_unsupported |
summary_template | string | No | Summary template identifier (max 50 characters) |
summary_mode | string | No | Summary mode: builtin (default, uses summary_template) or custom (uses summary_prompt). Omitted = uses summary_template |
summary_prompt | string | No | Full custom prompt for custom mode (max 3000 chars, fully replaces the built-in template). Required for custom, prohibited otherwise |
summary_prompt_slug | string | No | Custom identifier for custom mode (max 64 chars, pass-through, not validated). Required for custom, prohibited otherwise |
terminology | string | No | Terminology list (JSON object, format below) |
fuzzy_correction | string | No | Fuzzy correction rules (JSON object) |
translation_dict | string | No | Translation dictionary (JSON object, format below) |
callback_url | string | No | Webhook callback URL (max 2048 characters) |
Webhook notification: After you set
callback_url, an HTTP POST notification is sent automatically when audio processing completes or fails. You can also specify awebhook_urlin the API Key settings as a default callback. See the Webhook Guide.
Text Processing Parameter Formats
Terminology list (terminology): improves recognition accuracy for specific terms
{
"zh-TW": [
{ "term": "語者分離" },
{ "term": "即時轉錄" }
]
}
- Use the language code as the key and a term array as the value
term: term text (required, max 100 characters)- Up to 500 terms per language, and no more than 500 across all languages combined
- Up to 4000 fuzzy-correction rules per language, and no more than 4000 across all languages combined
The numbers above are defaults: the limit actually in force can be tuned per environment; always treat the 422 response message as authoritative.
Note: Both limits are enforced: exceeding 500 entries in a single language, or 500 across all languages combined, returns 422 with the actual count. Plan multi-language vocabularies against the combined total.
Fuzzy correction (fuzzy_correction): corrects misspellings that sound different from the term
Usually no manual configuration is needed — misspellings that sound the same are already covered by terminology. It is needed only when the misspelling and the correct term sound different.
{
"zh-TW": [
{ "correct": "語者分離", "incorrect": ["語這分離", "語者分力"] }
]
}
- Use the language code as the key and a correction rule array as the value
correct: the correct term (required, up to 200 characters)incorrect: a list of incorrect variants (conditionally required, each up to 200 characters)
Supplying only the correct term: when
correctis Chinese (contains Han characters),incorrectmay be omitted entirely — the system matches by pronunciation, and spellings in the transcript that sound the same or nearly the same are corrected back tocorrect.{ "fuzzy_correction": { "zh-TW": [{ "correct": "艾思通" }] } }No misspellings need to be listed above: 愛思通, 愛時通, 愛司東 and 愛似通 are all corrected. Only spellings that sound quite different (愛自動, say) or have a different number of syllables (愛松) still need to be listed in
incorrect.Note: Both conditions must hold: the language must be Chinese (
zh-TW,zh-CN,zh-HKand so on) andcorrectmust contain Han characters. Otherwiseincorrectremains required — omitting it in those cases would have no effect at all, and accepting it would leave you believing the setting took.
case_insensitive: whether this rule's variants match regardless of case (optional, defaults tofalse= exact-case matching)
When the same incorrect variant appears in more than one rule: collisions are resolved on
incorrect(the variant), not oncorrect. The case flag resolves to strict wins (if any rule leavescase_insensitiveoff, that variant is matched with exact case). Splitting onecorrectterm across several rules is therefore safe, as long as theirincorrectvariants do not overlap.
Translation dictionary (translation_dict): specifies how proper nouns are translated. Entries are grouped by language code, giving each language its own dictionary.
{
"en-US": [
{ "source": "語者分離", "target": "Speaker Diarization" }
]
}
- Top-level key: the target language code
source: the original term (required, up to 200 characters)target: the required translation for this language (required, up to 200 characters)case_sensitive: whether the entry applies only on an exact-case match (optional, defaults tofalse= case-insensitive)- Up to 3000 entries per language
The previous format is still supported: the earlier array-of-entries format (
[{ "source": ..., "translations": { "language code": ... } }]) is still accepted, with identical content and behavior. Existing integrations need no changes.
Case-flag comparison: the case switches in
fuzzy_correctionandtranslation_dicthave opposite field names, and their default value produces opposite behavior —
Block Field Default Default behavior fuzzy_correctioncase_insensitivefalseStrict (case-sensitive) translation_dictcase_sensitivefalsePermissive (case-insensitive) Both default to
false, yet one means strict and the other means permissive. Do not share a single variable between them or mirror one onto the other — getting it wrong produces no error at all, only matching behavior opposite to what you intended.Note: The translation dictionary guides the model through prompting rather than literal substitution, so it is best-effort and the case flag is likewise a hint. Use
fuzzy_correctionwhen you need deterministic replacement.
Credit check: Credit balance is checked automatically during upload. If credit is insufficient, an
auth_insufficient_crediterror (HTTP 402) is returned.Recommendation: Before uploading, use the
check-quotaAPI to pre-check whether credit is sufficient, to avoid discovering insufficient credit only after uploading a large file.
Request Example
Basic request
curl -X POST "https://vas-poc.vurbo.ai/api/v1/imports" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-F "file=@meeting.mp3" \
-F 'transcription_languages=["zh-TW"]' \
-F 'translation_languages=["en-US"]' \
-F "recognition_mode=multi_speaker"
Request with text processing settings
curl -X POST "https://vas-poc.vurbo.ai/api/v1/imports" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-F "file=@meeting.mp3" \
-F 'transcription_languages=["zh-TW"]' \
-F 'translation_languages=["en-US"]' \
-F "recognition_mode=multi_speaker" \
-F 'terminology={"zh-TW": [{"term": "語者分離"}]}' \
-F 'fuzzy_correction={"zh-TW": [{"correct": "語者分離", "incorrect": ["語這分離"]}]}' \
-F 'translation_dict={"en-US": [{"source": "語者分離", "target": "Speaker Diarization"}]}'
Success Response (HTTP 202)
{
"data": {
"import_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"stage": null,
"progress": 0,
"message": null,
"original_filename": "meeting.mp3",
"file_size": "15.2 MB",
"task_id": null,
"error_code": null,
"error_message": null,
"created_at": "2026-01-03T10:00:00.000Z",
"updated_at": "2026-01-03T10:00:00.000Z",
"downgraded_features": []
}
}
downgraded_features(v1.9.0): for unlimited plans that include audio import but not some sub-features (such as speaker diarizationspeaker_diarizationor translationtranslation), those sub-features are skipped and the import proceeds as usual; the skipped features are listed in this array. An empty array means nothing was downgraded.
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
import_file_too_large | 413 | File size exceeds the limit | Compress or split the file |
import_invalid_format | 415 | Unsupported audio format | Use mp3/wav/m4a format |
import_recognition_mode_unsupported | 422 | This recognition mode is not supported for file imports (multi_language, multi_channel); data.details carries field and supportedModes | Use single or multi_speaker |
auth_insufficient_credit | 402 | Insufficient credit | Top up your credit balance |
plan_feature_not_allowed | 403 | The unlimited plan does not include audio import | Upgrade the plan; query GET /api/v1/me/plan for the plan contents |
plan_daily_limit_reached | 402 | The plan's daily usage limit has been reached | Upload again after the plan's reset (the next day) |
GET /api/v1/imports/{importId} (Query Import Status)
Description
Query the processing status and progress of the specified import task.
Once the task created by an import is deleted, the corresponding import record is removed as well, and this request returns 404 import_not_found.
Use Cases
- Track the processing progress of an uploaded audio file
- Retrieve the task ID once processing completes
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
importId | string | Yes | Import ID (UUID) |
Request Example
curl -X GET "https://vas-poc.vurbo.ai/api/v1/imports/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
Success Response
{
"data": {
"import_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing",
"stage": "transcribing",
"progress": 45,
"message": "Recognizing speech...",
"original_filename": "meeting.mp3",
"file_size": "15.2 MB",
"task_id": null,
"error_code": null,
"error_message": null,
"created_at": "2026-01-03T10:00:00.000Z",
"updated_at": "2026-01-03T10:05:00.000Z"
}
}
| Field | Type | Description |
|---|---|---|
status | string | Status: pending / processing / completed / failed |
stage | string | Processing stage: converting / transcribing / translating / summarizing |
progress | integer | Progress percentage (0-100) |
task_id | string | The task ID after processing completes (usable with the Task API) |
error_code | string | The error code on failure |
error_message | string | The error message on failure (a general description without internal details; provide the import_id for troubleshooting) |
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
import_not_found | 404 | Import task not found | Verify that importId is correct; if the task created by the import has been deleted, the import record is removed as well |
GET /api/v1/imports (List Imports)
Description
Retrieve the user's import task list (paginated). Import records whose tasks have been deleted do not appear in the list.
Use Cases
- Display the import history
- View the status of all import tasks
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
per_page | integer | No | Items per page (default 20) |
Request Example
curl -X GET "https://vas-poc.vurbo.ai/api/v1/imports?per_page=20" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
Success Response
{
"data": [
{
"import_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"original_filename": "meeting.mp3",
"file_size": "15.2 MB",
"task_id": "660e8400-e29b-41d4-a716-446655440001",
"created_at": "2026-01-03T10:00:00.000Z"
}
],
"meta": {
"current_page": 1,
"last_page": 5,
"per_page": 20,
"total": 100
}
}
Audio API
Audio file streaming playback, with support for HTTP Range Requests.
GET /api/v1/sse/audio/{taskId} (Audio Streaming Playback)
Description
Stream the recording file of the specified task, with support for HTTP Range Requests to enable seek playback.
Note: Although the path contains
/sse/, this endpoint returns an audio file (not an SSE stream).
Use Cases
- Play recording audio
- Support seeking the playback position
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
taskId | string | Yes | Recording ID (i.e. recordings.id, UUID) |
Request Example
curl -X GET "https://vas-poc.vurbo.ai/api/v1/sse/audio/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
Response Format
Full file (HTTP 200):
HTTP/1.1 200 OK
Content-Type: audio/mp4
Content-Length: 1234567
Accept-Ranges: bytes
Cache-Control: no-cache
Note: All recording audio files are returned as an M4A container (AAC encoding). Content-Type is fixed to
audio/mp4.
Partial file (HTTP 206 - Range Request):
HTTP/1.1 206 Partial Content
Content-Type: audio/mp4
Content-Length: 1024
Content-Range: bytes 0-1023/1234567
Accept-Ranges: bytes
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
recording_not_found | 404 | Recording not found | Verify that taskId is correct |
recording_audio_not_ready | 422 | The audio file has not finished uploading | Retry later |
storage_download_failed | 500 | Storage service download failed | Retry later |
Frontend Example
async function playAudio(taskId, apiKey) {
const response = await fetch(
`https://vas-poc.vurbo.ai/api/v1/sse/audio/${taskId}`,
{
headers: {
'X-API-Key': apiKey
}
}
);
const blob = await response.blob();
const audioUrl = URL.createObjectURL(blob);
const audio = new Audio(audioUrl);
audio.play();
}
TTS API
The TTS (Text-to-Speech) API provides lookup functions related to speech synthesis.
GET /api/v1/tts/voices (List TTS Voices)
Description
Retrieve the list of available TTS voices for the specified language. Each language has multiple voices to choose from, including different genders and styles.
Use Cases
- Let users choose their preferred TTS voice
- Display the available voice options
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
language | string | Yes | Language code (e.g. en-US) |
Request Example
curl -X GET "https://vas-poc.vurbo.ai/api/v1/tts/voices?language=en-US" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
Success Response
{
"data": {
"language": "en-US",
"voices": [
{
"voice_name": "en-US-JennyNeural",
"display_name": "Jenny",
"gender": "Female",
"is_default": true,
"sample_url": "https://vas-poc.vurbo.ai/api/v1/tts/voices/en-US-JennyNeural/sample"
},
{
"voice_name": "en-US-GuyNeural",
"display_name": "Guy",
"gender": "Male",
"is_default": false,
"sample_url": "https://vas-poc.vurbo.ai/api/v1/tts/voices/en-US-GuyNeural/sample"
},
{
"voice_name": "en-US-AriaNeural",
"display_name": "Aria",
"gender": "Female",
"is_default": false,
"sample_url": "https://vas-poc.vurbo.ai/api/v1/tts/voices/en-US-AriaNeural/sample"
},
{
"voice_name": "en-US-DavisNeural",
"display_name": "Davis",
"gender": "Male",
"is_default": false,
"sample_url": "https://vas-poc.vurbo.ai/api/v1/tts/voices/en-US-DavisNeural/sample"
},
{
"voice_name": "en-US-SaraNeural",
"display_name": "Sara",
"gender": "Female",
"is_default": false,
"sample_url": "https://vas-poc.vurbo.ai/api/v1/tts/voices/en-US-SaraNeural/sample"
},
{
"voice_name": "en-US-TonyNeural",
"display_name": "Tony",
"gender": "Male",
"is_default": false,
"sample_url": "https://vas-poc.vurbo.ai/api/v1/tts/voices/en-US-TonyNeural/sample"
}
]
}
}
| Field | Type | Description |
|---|---|---|
voice_name | string | Voice identifier (used by the API) |
display_name | string | Voice display name |
gender | string | Gender: Female / Male |
is_default | boolean | Whether this is the default voice for the language |
sample_url | string | Voice sample audio URL (playable directly for preview) |
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
validation_failed | 400 | Missing language parameter or invalid parameter format | Provide a valid language parameter |
When this endpoint receives an unsupported language code it returns HTTP 200 with an empty
voicesarray instead of an error -- this includes locales that have voices available but cannot be used as a TTS target because they lack transcription support. For valid codes, see the supported language list.
GET /api/v1/tts/voices/{voiceName}/sample (Get Voice Sample Audio)
Description
Retrieve the sample audio file (MP3 format) for the specified voice. The first request synthesizes it in real time and caches it; subsequent requests are served directly from the cache.
This endpoint does not count toward TTS charges.
Use Cases
- Let users preview a voice before choosing it
- Provide voice browsing and comparison
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
voiceName | string | Yes | Voice name (e.g. en-US-JennyNeural) |
Request Example
curl -X GET "https://vas-poc.vurbo.ai/api/v1/tts/voices/zh-TW-HsiaoChenNeural/sample" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
--output sample.mp3
Success Response
The response is binary MP3 audio data (not JSON).
| Header | Value |
|---|---|
Content-Type | audio/mpeg |
Content-Length | Audio file size (bytes) |
Cache-Control | public, max-age=86400 |
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
tts_voice_not_found | 404 | Voice not found, or the voice's language cannot be used as a TTS target (any voice not listed by GET /api/v1/tts/voices is treated as not found) | Use a voice_name returned by GET /api/v1/tts/voices?language={code} |
tts_sample_generation_failed | 500 | Voice sample generation failed | Retry later |
| - | 429 | Request rate too high | Wait and retry (limit 30 per minute) |
Rate Limiting
30 requests per minute per user. When the limit is exceeded, HTTP 429 is returned.
Broadcasts API
The broadcast API manages real-time subtitle streaming, including creating, querying, updating, and revoking broadcasts.
GET /api/v1/broadcasts (List Broadcasts)
Description
Query the broadcast list owned by the current API Key holder (excluding revoked channels), with pagination support.
Use Cases
- View all created broadcasts
- Manage multiple broadcast channels
- Monitor broadcast status
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
per_page | integer | No | Items per page (default 20) |
page | integer | No | Page number (default 1) |
Request Example
curl -X GET "https://vas-poc.vurbo.ai/api/v1/broadcasts?per_page=10&page=1" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
Success Response (HTTP 200)
{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"token": "a3f9",
"name": "My Broadcast Channel",
"share_url": "https://vas-poc.vurbo.ai/broadcast/a3f9",
"transcription_language": "zh-TW",
"translation_languages": ["en-US", "ja-JP"],
"tts_config": null,
"speaker_diarization": false,
"summary_template": null,
"summary_language": null,
"max_viewers": 100,
"access_type": "public",
"pass_code": null,
"status": "pending",
"is_live": false,
"session_id": null,
"current_recording_id": null,
"recordings_count": 0,
"peak_viewers": 0,
"total_viewers": 0,
"duration_ms": 0,
"duration_formatted": "0:00",
"started_at": null,
"ended_at": null,
"revoked_at": null,
"created_at": "2026-01-03T10:00:00.000Z"
}
],
"meta": {
"current_page": 1,
"last_page": 5,
"per_page": 10,
"total": 50
}
}
For an explanation of the response fields, see the response fields of Create Broadcast.
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
auth_missing_api_key | 401 | API Key not provided | Make sure the header includes the API Key |
auth_invalid_api_key | 401 | Invalid API Key | Verify that the API Key is correct |
POST /api/v1/broadcasts (Create Broadcast)
Description
Create a new broadcast session for real-time subtitle streaming. After creation, a share link is generated, and viewers can receive real-time subtitles and translations through this link.
The full request/response spec (including the plural
transcription_languagesfield, the 12-language translation limit, and the error codes for each violation) is in reference/rest/broadcasts.md. The singulartranscription_languageshown here is a backward-compatible field (equal to the first array element).
Use Cases
- Create real-time subtitles for a lecture/talk
- Create a real-time translation stream for a meeting
- Create real-time subtitles for a livestream
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
transcription_language | string | Yes | Transcription language code (e.g. zh-TW) |
translation_languages | string[] | No | Array of translation language codes |
name | string | No | Channel name (max 100 characters; cannot be changed after creation and is not used as the recording name) |
access_type | string | No | Access type: public (default) or password |
pass_code | string | Conditional | Password (required when access_type is password, 4-12 characters; letters, digits and common punctuation only (no Chinese characters, no spaces)) |
max_viewers | integer | No | Maximum number of viewers (1 to the account viewer limit; defaults to that limit when omitted) |
speaker_diarization | boolean | No | Speaker diarization (true or false, default false) |
tts_config | object | No | TTS default settings (key is the language code) |
tts_config.*.voice | string | No | TTS voice name (uses the default voice if not specified) |
summary_template | string | No | Summary template slug (max 50 characters, must be an enabled summary category template) |
summary_language | string | No | Summary output language; must be a language code from the supported language list (defaults to transcription_language if not specified) |
callback_url | string | No | Webhook callback URL (max 2048 characters) |
Webhook notification: After you set
callback_url, an HTTP POST notification is sent automatically when broadcast recording processing completes or fails. You can also specify awebhook_urlin the API Key settings as a default callback. See the Webhook Guide.
Request Example
curl -X POST "https://vas-poc.vurbo.ai/api/v1/broadcasts" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-H "Content-Type: application/json" \
-d '{
"transcription_language": "zh-TW",
"translation_languages": ["en-US", "ja-JP"],
"name": "My Broadcast Channel",
"access_type": "public",
"max_viewers": 50,
"tts_config": {
"en-US": {"voice": "en-US-JennyNeural"},
"ja-JP": {"voice": "ja-JP-NanamiNeural"}
},
"summary_template": "lecture",
"summary_language": "zh-TW",
"callback_url": "https://your-server.com/webhooks/vas"
}'
Success Response (HTTP 201)
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"token": "a3f9",
"name": "My Broadcast Channel",
"share_url": "https://vas-poc.vurbo.ai/broadcast/a3f9",
"transcription_language": "zh-TW",
"translation_languages": ["en-US", "ja-JP"],
"tts_config": {
"en-US": {"voice": "en-US-JennyNeural"},
"ja-JP": {"voice": "ja-JP-NanamiNeural"}
},
"speaker_diarization": false,
"summary_template": "lecture",
"summary_language": "zh-TW",
"max_viewers": 100,
"access_type": "public",
"pass_code": null,
"status": "pending",
"is_live": false,
"session_id": null,
"current_recording_id": null,
"recordings_count": 0,
"peak_viewers": 0,
"total_viewers": 0,
"duration_ms": 0,
"duration_formatted": "0:00",
"started_at": null,
"ended_at": null,
"revoked_at": null,
"created_at": "2026-01-03T10:00:00.000Z"
}
}
| Field | Type | Description |
|---|---|---|
id | string | Broadcast ID (UUID) |
token | string | Share token (4-character short code, character set a-z0-9) |
name | string | Broadcast name |
share_url | string | Default share URL (not an openable viewer page; see the Broadcast Guide) |
transcription_language | string | Transcription language |
translation_languages | array | Translation language list |
tts_config | object | TTS default settings (key is the language code) |
speaker_diarization | boolean | Speaker diarization toggle |
summary_template | string | Summary template slug (null means not set) |
summary_language | string | Summary output language (null defaults to transcription_language) |
max_viewers | integer | Maximum number of viewers |
access_type | string | Access type: public or password |
pass_code | string | Plaintext password (set when access_type is password, otherwise null) |
status | string | Status (see below) |
is_live | boolean | Whether currently live (true when in the active or paused state) |
session_id | string | WebSocket Session ID |
current_recording_id | string | Current recording UUID: has a value only while live, and is the recording of this go-live (the new recording after a takeover); null during standby or when not live |
recordings_count | integer | Number of historical recordings |
peak_viewers | integer | Historical peak viewer count |
total_viewers | integer | Cumulative viewer count |
duration_ms | integer | Broadcast duration (milliseconds) |
duration_formatted | string | Formatted duration (min:sec) |
started_at | string | Start time (ISO 8601) |
ended_at | string | End time (ISO 8601) |
revoked_at | string | Revocation time (ISO 8601) |
created_at | string | Creation time (ISO 8601) |
Broadcast Status
| Status | Description |
|---|---|
pending | Waiting to start (created, not yet started) |
active | Broadcasting |
paused | Paused |
ended | Ended |
revoked | Revoked |
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
auth_missing_api_key | 401 | API Key not provided | Make sure the header includes the API Key |
auth_invalid_api_key | 401 | Invalid API Key | Verify that the API Key is correct |
validation_failed | 422 | Parameter validation failed | Verify that the parameter format is correct |
plan_feature_not_allowed | 403 | The unlimited plan does not include broadcasting (broadcasting is never included in plans) | Broadcasts are billed in credits; use a credit-based API Key |
GET /api/v1/broadcasts/{id} (Query Broadcast Status)
Description
Query detailed information and the current status of the specified broadcast.
Use Cases
- Check whether the broadcast has started
- Monitor the viewer count
- Confirm the broadcast status
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Broadcast ID (UUID) |
Request Example
curl -X GET "https://vas-poc.vurbo.ai/api/v1/broadcasts/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
Success Response (HTTP 200)
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"token": "a3f9",
"name": "My Broadcast Channel",
"share_url": "https://vas-poc.vurbo.ai/broadcast/a3f9",
"transcription_language": "zh-TW",
"translation_languages": ["en-US", "ja-JP"],
"tts_config": {
"en-US": {"voice": "en-US-JennyNeural"},
"ja-JP": {"voice": "ja-JP-NanamiNeural"}
},
"speaker_diarization": true,
"summary_template": "lecture",
"summary_language": "zh-TW",
"max_viewers": 100,
"access_type": "public",
"status": "active",
"is_live": true,
"session_id": "ws_session_xyz",
"current_recording_id": "660e8400-e29b-41d4-a716-446655440001",
"recordings_count": 1,
"peak_viewers": 25,
"total_viewers": 30,
"duration_ms": 1800000,
"duration_formatted": "30:00",
"started_at": "2026-01-03T10:00:00.000Z",
"ended_at": null,
"revoked_at": null,
"created_at": "2026-01-03T09:55:00.000Z"
}
}
For an explanation of the response fields, see the response fields of Create Broadcast.
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
broadcast_session_not_found | 404 | The specified broadcast was not found | Verify that the broadcast ID is correct |
PATCH /api/v1/broadcasts/{id} (Update Broadcast Settings)
Description
Dynamically update broadcast settings, including access type, maximum viewer count, transcription language, and translation languages. This API can be called while a broadcast is in progress (active or paused state) to adjust settings in real time.
Use Cases
- Change a public broadcast to password protected
- Change a password-protected broadcast to public
- Adjust the maximum viewer count
- Change the transcription language (e.g. from Chinese to English)
- Add or remove translation languages
- Turn speaker diarization on or off
- Dynamically adjust settings while live
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Broadcast ID (path parameter) |
access_type | string | No | Access type: public or password |
pass_code | string | Conditional | Password (4-12 characters; letters, digits and common punctuation only (no Chinese characters, no spaces), required when access_type is password) |
max_viewers | integer | No | Maximum number of viewers (1 to the account viewer limit) |
transcription_language | string | No | Transcription language (e.g. zh-TW, en-US, ja-JP) |
translation_languages | array | No | Translation language list (e.g. ["en-US", "ja-JP"]) |
speaker_diarization | boolean | No | Speaker diarization toggle (true or false) |
tts_config | object | No | TTS default settings (overrides existing settings; null means clear) |
summary_template | string | No | Summary template slug (max 50 characters, an empty string "" means clear) |
summary_language | string | No | Summary output language; must be a language code from the supported language list (an empty string "" means clear) |
Note: If no updatable field is provided, the request returns 200 and nothing changes. The channel name cannot be changed after creation; a
namefield is ignored. Passingnullfortts_configclears the settings; omitting it means no change.
Request Example
# Change to password protected
curl -X PATCH "https://vas-poc.vurbo.ai/api/v1/broadcasts/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-H "Content-Type: application/json" \
-d '{
"access_type": "password",
"pass_code": "mySecret123"
}'
# Adjust the maximum viewer count
curl -X PATCH "https://vas-poc.vurbo.ai/api/v1/broadcasts/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-H "Content-Type: application/json" \
-d '{
"max_viewers": 200
}'
# Change the transcription and translation languages
curl -X PATCH "https://vas-poc.vurbo.ai/api/v1/broadcasts/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-H "Content-Type: application/json" \
-d '{
"transcription_language": "en-US",
"translation_languages": ["zh-TW", "ja-JP", "ko-KR"]
}'
# Turn on speaker diarization
curl -X PATCH "https://vas-poc.vurbo.ai/api/v1/broadcasts/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-H "Content-Type: application/json" \
-d '{
"speaker_diarization": true
}'
# Update the TTS default settings
curl -X PATCH "https://vas-poc.vurbo.ai/api/v1/broadcasts/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-H "Content-Type: application/json" \
-d '{
"tts_config": {
"zh-TW": {"voice": "zh-TW-HsiaoChenNeural"},
"ja-JP": {"voice": "ja-JP-NanamiNeural"}
}
}'
Success Response (HTTP 200)
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"token": "a3f9",
"name": "My Broadcast Channel",
"share_url": "https://vas-poc.vurbo.ai/broadcast/a3f9",
"transcription_language": "zh-TW",
"translation_languages": ["en-US", "ja-JP"],
"tts_config": {
"en-US": {"voice": "en-US-JennyNeural"},
"ja-JP": {"voice": "ja-JP-NanamiNeural"}
},
"speaker_diarization": true,
"summary_template": "lecture",
"summary_language": "zh-TW",
"access_type": "password",
"pass_code": "mySecret123",
"max_viewers": 200,
"status": "active",
"is_live": true,
"session_id": "ws_session_xyz",
"current_recording_id": "660e8400-e29b-41d4-a716-446655440001",
"recordings_count": 1,
"peak_viewers": 25,
"total_viewers": 30,
"duration_ms": 1800000,
"duration_formatted": "30:00",
"started_at": "2026-01-03T10:00:00.000Z",
"ended_at": null,
"revoked_at": null,
"created_at": "2026-01-03T09:55:00.000Z"
}
}
For an explanation of the response fields, see the response fields of Create Broadcast.
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
broadcast_session_not_found | 404 | The specified broadcast was not found | Verify that the broadcast ID is correct |
validation_failed | 422 | Parameter validation failed | Verify that the parameter format is correct |
DELETE /api/v1/broadcasts/{id} (Revoke Broadcast)
Description
Revoke a broadcast that has not yet started. Only broadcasts in the pending state can be revoked.
Use Cases
- Cancel a broadcast that has not yet started
- Clean up broadcasts you no longer need
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Broadcast ID (UUID) |
Request Example
curl -X DELETE "https://vas-poc.vurbo.ai/api/v1/broadcasts/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
Success Response (HTTP 200)
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"token": "a3f9",
"name": "My Broadcast Channel",
"share_url": "https://vas-poc.vurbo.ai/broadcast/a3f9",
"transcription_language": "zh-TW",
"translation_languages": ["en-US", "ja-JP"],
"tts_config": null,
"speaker_diarization": false,
"summary_template": null,
"summary_language": null,
"max_viewers": 100,
"access_type": "public",
"status": "revoked",
"is_live": false,
"session_id": null,
"current_recording_id": null,
"recordings_count": 0,
"peak_viewers": 0,
"total_viewers": 0,
"duration_ms": 0,
"duration_formatted": "0:00",
"started_at": null,
"ended_at": null,
"revoked_at": "2026-01-03T10:05:00.000Z",
"created_at": "2026-01-03T10:00:00.000Z"
}
}
For an explanation of the response fields, see the response fields of Create Broadcast.
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
broadcast_session_not_found | 404 | The specified broadcast was not found | Verify that the broadcast ID is correct |
broadcast_cannot_revoke | 422 | Only broadcasts in the pending state can be revoked | Check the current broadcast status |
DELETE /api/v1/broadcasts/batch (Batch Revoke Broadcasts)
Description
Batch revoke multiple broadcasts. Only broadcasts in the pending state are revoked; IDs in other states are ignored. Up to 100 can be operated on per request.
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
ids | body | array | Yes | Array of broadcast IDs (each element a UUID, up to 100) |
Request Example
curl -X DELETE "https://vas-poc.vurbo.ai/api/v1/broadcasts/batch" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-H "Content-Type: application/json" \
-d '{
"ids": [
"550e8400-e29b-41d4-a716-446655440000",
"6ba7b810-9dad-11d1-80b4-00c04fd430c8"
]
}'
Success Response (HTTP 200)
{
"data": {
"affected_count": 2
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
data.affected_count | number | Number of broadcasts actually revoked (only pending-state broadcasts are counted) |
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
validation_failed | 422 | Parameter validation failed | Make sure ids is an array of UUIDs with no more than 100 entries |
Viewer API
The viewer-side API does not require API Key authentication. It is used for viewers to view broadcast information and verify passwords.
Both endpoints can be called directly from a web page on any domain, so your viewer page can live on your own domain; do not send credentials with the request. See Calling from a Browser.
GET /api/v1/viewer/broadcasts/{token} (Get Broadcast Public Info)
Description
Retrieve the public information of the specified broadcast, for the viewer side to display channel information.
Use Cases
- Display channel information before a viewer enters the broadcast page
- Determine whether a password is required
Authentication
No authentication required
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | Yes | Broadcast token (4-character short code a-z0-9) |
Request Example
curl -X GET "https://vas-poc.vurbo.ai/api/v1/viewer/broadcasts/a3f9"
Success Response (HTTP 200)
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "My Broadcast Channel",
"access_type": "password",
"requires_password": true,
"status": "active",
"is_live": true,
"transcription_language": "zh-TW",
"transcription_languages": ["zh-TW", "en-US"],
"translation_languages": ["en-US", "ja-JP"],
"tts_languages": ["en-US", "ja-JP"],
"tts_voices": {
"en-US": [
{ "voice_name": "en-US-JennyNeural", "display_name": "Jenny", "gender": "Female" },
{ "voice_name": "en-US-GuyNeural", "display_name": "Guy", "gender": "Male" }
],
"ja-JP": [
{ "voice_name": "ja-JP-NanamiNeural", "display_name": "Nanami", "gender": "Female" },
{ "voice_name": "ja-JP-KeitaNeural", "display_name": "Keita", "gender": "Male" }
]
}
}
}
| Field | Type | Description |
|---|---|---|
id | string | Broadcast ID (UUID) |
name | string | Channel name |
access_type | string | Access type: public/password |
requires_password | boolean | Whether password verification is required |
status | string | Broadcast status |
is_live | boolean | Whether currently live |
transcription_language | string | Transcription language (backward compatible; equals the first element of transcription_languages) |
transcription_languages | array | List of transcription languages (string[], up to 10, distinct) |
translation_languages | array | Translation language list |
tts_languages | array | Languages for which the host has enabled voice playback; an empty array when the channel is not live |
tts_voices | object | TTS voice list for each translation language |
tts_voices structure:
tts_voices is an object keyed by language code, where each language contains an array of available voices:
| Field | Type | Description |
|---|---|---|
voice_name | string | Voice name (for API use) |
display_name | string | Display name |
gender | string | Gender: Female/Male |
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
broadcast_token_invalid | 401 | Token invalid or not found | Verify that the token is correct |
broadcast_token_revoked | 401 | Broadcast has been revoked | This broadcast is no longer available |
too_many_requests | 429 | Too many requests: per channel, the limit per minute is about twice the channel's maximum number of viewers (minimum 200); too many lookups of nonexistent channels from the same source also return 429 temporarily. See Rate Limits | Wait for the number of seconds in the Retry-After response header, then retry |
POST /api/v1/viewer/broadcasts/{token}/verify (Password Verification)
Description
Verify the password and obtain a viewer access token. The obtained viewer_access_token is used to connect to the SSE real-time subtitle stream.
Use Cases
- Verify the password before a viewer enters a password-protected broadcast
- Obtain the viewer_access_token required for the SSE connection
Authentication
No authentication required
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | Yes | Broadcast token (path parameter) |
password | string | Yes | Channel password (max 12 characters) |
Request Example
curl -X POST "https://vas-poc.vurbo.ai/api/v1/viewer/broadcasts/a3f9/verify" \
-H "Content-Type: application/json" \
-d '{
"password": "mySecret123"
}'
Success Response (HTTP 200)
Correct password:
{
"data": {
"viewer_access_token": "aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vWaB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW",
"expires_at": "2026-01-04T10:00:00.000Z"
}
}
Public channel (no password required):
{
"data": {
"viewer_access_token": null,
"message": "此頻道為公開,不需要密碼驗證"
}
}
The
messagetext is returned in Traditional Chinese and is meant for display only -- do not parse or match on it.
| Field | Type | Description |
|---|---|---|
viewer_access_token | string | Viewer access token (valid for 24 hours) |
expires_at | string | Token expiration time (ISO 8601) |
Usage: After obtaining the
viewer_access_token, include it when connecting to SSE:GET /broadcast/{token}/text?viewer_access_token=xxx
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
broadcast_token_invalid | 401 | Invalid token | Verify that the token is correct |
broadcast_token_revoked | 401 | Broadcast has been revoked | This broadcast is no longer available |
broadcast_password_incorrect | 401 | Incorrect password | Re-enter the correct password |
validation_failed | 422 | Parameter validation failed | Verify that the password format is correct |
too_many_requests | 429 | Too many requests, or temporarily locked after too many incorrect passwords (30 from the same source within 5 minutes, or 100 for the same channel within 5 minutes). See Rate Limits | Wait for the number of seconds in the Retry-After response header, then retry; while locked, even the correct password must wait |
Recording Speaker Editing API
The Recording Speaker editing API provides transcript speaker editing for offline recordings, behaving consistently with the Speaker editing feature of the real-time mode WebSocket.
Limitation: This API applies only to recordings in multi-speaker recognition mode (
multi_speaker). Single-speaker recordings return aspeaker_diarization_requirederror.
PATCH /api/v1/tasks/{taskId}/speakers/rename (Globally Rename Speaker)
Description
Globally rename the specified speaker ID to a new name. This operation updates the speakerAliases mapping and updates the speaker field of all transcript entries that use that speaker ID to the new name.
Use Cases
- Change an automatically recognized speaker ID (e.g.
Guest-1) to a real name - Uniformly change the display name of a speaker
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
taskId | string | Yes | Task UUID (path parameter) |
speaker_id | string | Yes | The original speaker ID (e.g. Guest-1); the current display label is also accepted for consecutive renames; max 100 characters |
new_label | string | Yes | The new display label; max 100 characters, must not contain control characters (\x00-\x1F, \x7F) or line breaks (it is written to the transcript and export files) |
Request Example
curl -X PATCH "https://vas-poc.vurbo.ai/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000/speakers/rename" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-H "Content-Type: application/json" \
-d '{
"speaker_id": "Guest-1",
"new_label": "Manager Wang"
}'
Success Response (HTTP 200)
{
"data": {
"speaker_id": "Guest-1",
"new_label": "Manager Wang",
"affected_sids": [1, 3, 5, 8, 12]
}
}
| Field | Type | Description |
|---|---|---|
speaker_id | string | The resolved original speaker ID (even if the request sends a display label, the response is the original ID) |
new_label | string | The new display label |
affected_sids | array<int> | List of affected sentence SIDs |
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
recording_not_found | 404 | The specified recording was not found | Verify that taskId is correct |
speaker_transcript_not_found | 404 | Transcript not found | Confirm the recording has finished transcribing |
speaker_diarization_required | 422 | This feature requires diarization recording | Applies only to recordings in multi-speaker recognition mode |
speaker_name_empty | 422 | new_label is empty | Provide a valid new_label |
validation_failed | 422 | Parameter validation failed | Check the length and characters of speaker_id / new_label (must not contain control characters) |
transcript_revision_conflict | 409 | Another write to the same transcript is in progress | Retry shortly; this change did not take effect |
storage_upload_failed | 500 | Failed to write the transcript back to storage | Retry shortly; this change did not take effect |
PATCH /api/v1/tasks/{taskId}/speakers/reassign (Reassign a Single Sentence Speaker)
Description
Change the speaker identity of a single sentence, assigning the sentence to an existing speaker.
Use Cases
- Correct speaker recognition errors
- Reassign a sentence to the correct speaker
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
taskId | string | Yes | Task UUID (path parameter) |
sid | integer | Yes | Sentence number |
target_speaker_id | string | Yes | The target speaker's original ID (taken from init_sentence.speaker_id; display labels are not accepted); max 100 characters |
Request Example
curl -X PATCH "https://vas-poc.vurbo.ai/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000/speakers/reassign" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-H "Content-Type: application/json" \
-d '{
"sid": 5,
"target_speaker_id": "Guest-2"
}'
Success Response (HTTP 200)
{
"data": {
"sid": 5,
"old_speaker_id": "Guest-1",
"new_speaker_id": "Guest-2",
"new_speaker_label": "Lily Lee"
}
}
| Field | Type | Description |
|---|---|---|
sid | integer | The ID of the modified sentence |
old_speaker_id | string | The original speaker ID |
new_speaker_id | string | The new original speaker ID |
new_speaker_label | string | The new speaker display label (after applying speaker_aliases; equals new_speaker_id when there is no alias) |
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
recording_not_found | 404 | The specified recording was not found | Verify that taskId is correct |
speaker_transcript_not_found | 404 | Transcript not found | Confirm the recording has finished transcribing |
speaker_diarization_required | 422 | This feature requires diarization recording | Applies only to recordings in multi-speaker recognition mode |
speaker_sid_not_found | 422 | The specified sentence was not found | Confirm that sid exists |
speaker_not_found | 422 | The specified speaker was not found | Confirm that target_speaker_id exists |
invalid_data | 422 | Creating a new speaker is not supported | Use an existing speaker ID |
validation_failed | 422 | Parameter validation failed | Verify that the parameter format is correct |
transcript_revision_conflict | 409 | Another write to the same transcript is in progress | Retry shortly; this change did not take effect |
storage_upload_failed | 500 | Failed to write the transcript back to storage | Retry shortly; this change did not take effect |
PATCH /api/v1/tasks/{taskId}/speakers/merge (Merge Speakers)
Description
Assign all sentences of the source speaker to the target speaker; the source's alias (if any) is transferred to the target. This applies when the diarization model mistakenly identifies the same person as two speakers.
vs. reassign:
reassignchanges only a single sentence;mergechanges all sentences of the speaker. vs. rename:renamechanges only the display name;mergeconsolidates multiple speakers.
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
taskId | string | Yes | Task ID (UUID, path parameter) |
source_speaker_id | string | Yes | The original speaker ID or current display label to be merged (e.g. Guest-2 or Manager Wang), max 100 characters |
target_speaker_id | string | Yes | The original ID or current display label of the merge target speaker (e.g. Guest-1), max 100 characters |
Request Example
curl -X PATCH "https://vas-poc.vurbo.ai/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000/speakers/merge" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-H "Content-Type: application/json" \
-d '{
"source_speaker_id": "Guest-2",
"target_speaker_id": "Guest-1"
}'
Success Response (HTTP 200)
{
"data": {
"source_speaker_id": "Guest-2",
"target_speaker_id": "Guest-1",
"target_speaker_label": "Manager Wang",
"affected_sids": [3, 5, 7]
}
}
| Field | Type | Description |
|---|---|---|
source_speaker_id | string | The original speaker ID to be merged (resolved back to the original ID even if the request sends a display label) |
target_speaker_id | string | The original speaker ID of the merge target |
target_speaker_label | string | The target speaker display label (after applying speaker_aliases; equals the original ID when there is no alias) |
affected_sids | array<int> | List of affected sentence SIDs: sentences that belonged to the source speaker, plus the target speaker's existing sentences whose display name changed because of the merge (for example, when the source speaker's custom name is carried over to the target) |
Error Responses
| Error Code | HTTP Status | Description | Recommended Action |
|---|---|---|---|
merge_speakers_same_id | 400 | Source and target are the same speaker | Provide different speaker IDs |
speaker_name_empty | 422 | Source or target is an empty string | Provide a valid speaker ID |
speaker_not_found | 422 | Source or target does not exist in the recording | Verify that the speaker ID is correct |
recording_not_found | 404 | Recording not found | Verify that taskId is correct |
speaker_transcript_not_found | 404 | Transcript not found | Confirm the recording has finished transcribing |
speaker_diarization_required | 422 | The recording is not in multi-speaker mode | Applies only to recognition_mode: multi_speaker |
validation_failed | 422 | Parameter validation failed | Confirm both source and target are provided |
transcript_revision_conflict | 409 | Another write to the same transcript is in progress | Retry shortly; this change did not take effect |
storage_upload_failed | 500 | Failed to write the transcript back to storage | Retry shortly; this change did not take effect |
For the full specification, see reference/rest/speakers.md.
Recording Entry Editing API (added in v1.4.0)
For historical recordings, this provides an API to correct the STT original text of a single sentence. After correction, you can call GET /api/v1/sse/recordings/{taskId}/entries/{sid}/retranslate to retranslate automatically. For the full specification, see reference/rest/entries.md.
PATCH /api/v1/tasks/{taskId}/entries/{sid} (Edit a Single Sentence's Original Text)
Description
Edit the original text (original_text) of a single sentence in a historical recording. On the first edit, the system automatically backs up the raw STT output to original_text_raw, and writes original_text_edited_at and the transcript revision. This changes only the original text, not the translation — to retranslate, call the corresponding SSE endpoint.
Limitations
- Only recordings with
processing_status === completedare allowed; in-progress recordings returnrecording_not_completed - Optimistic locking: you can pass
expected_revision; a mismatch returns 409transcript_revision_conflict
Authentication
Header: X-API-Key
Request Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
taskId | path | string | Yes | Task ID (UUID) |
sid | path | number | Yes | Sentence ID (1-based) |
original_text | body | string | Yes | The corrected original text, 1–2000 characters |
expected_revision | body | number | No | Optimistic lock; the current transcript revision |
Request Example
curl -X PATCH "https://vas-poc.vurbo.ai/api/v1/tasks/{taskId}/entries/5" \
-H "X-API-Key: vas_xxx" \
-H "Content-Type: application/json" \
-d '{ "original_text": "The corrected text", "expected_revision": 3 }'
Success Response (HTTP 200)
{
"data": {
"sid": 5,
"original_text": "The corrected text",
"original_text_raw": "The raw STT output",
"original_text_edited_at": "2026-05-06T10:30:00.000000Z",
"translated_texts": { "en-US": "The outdated old translation" },
"revision": 4
}
}
Existing translations are not updated automatically; after receiving the response, the frontend should call
GET /api/v1/sse/recordings/{taskId}/entries/{sid}/retranslateto retranslate.
Error Responses
| Error Code | HTTP | Description |
|---|---|---|
recording_not_found | 404 | The recording does not exist or does not belong to the user |
recording_not_completed | 422 | The recording has not finished processing |
entry_not_found | 404 | The specified sentence was not found |
entry_text_empty | 422 | The original text is empty |
entry_text_too_long | 422 | The original text exceeds 2000 characters |
transcript_revision_conflict | 409 | The revision does not match, or another write to the same transcript is in progress |
speaker_transcript_not_found | 404 | The transcript was not found |
For the full specification and an "edit + automatic retranslate" workflow example, see reference/rest/entries.md.
Summary Template API
The summary template API provides a list of available summary templates, used to choose the summary style when importing audio files.
GET /api/v1/summary-templates (List Summary Templates)
For the full schema, see reference/rest/summary-templates.md.
Description
Retrieve the list of available summary templates. Each template represents a different summary style, suitable for different scenarios (such as meetings, medical consultations, legal consultations, etc.).
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Location | Type | Required | Default | Description |
|---|---|---|---|---|---|
category | query | string | No | summary | Template category filter: summary / medical / legal / all |
Request Example
curl -X GET "https://vas-poc.vurbo.ai/api/v1/summary-templates?category=medical" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
Success Response
{
"data": [
{ "slug": "general", "name": "General Summary", "description": "...", "category": "summary" },
{ "slug": "meeting", "name": "Meeting Summary", "description": "...", "category": "summary" },
{ "slug": "meeting_minutes", "name": "Meeting Minutes", "description": "...", "category": "summary" },
{ "slug": "speech", "name": "Speech Summary", "description": "...", "category": "summary" },
{ "slug": "interview", "name": "Interview Summary", "description": "...", "category": "summary" },
{ "slug": "course", "name": "Course Summary", "description": "...", "category": "summary" }
]
}
| Field | Type | Description |
|---|---|---|
slug | string | Template identifier (used in API parameters) |
name | string | Template name |
description | string | Template description (may be null) |
category | string | Template category (summary / medical / legal) |
Error Responses
| Error Code | HTTP | Description | Recommended Action |
|---|---|---|---|
auth_missing_api_key | 401 | API Key not provided | Make sure the header includes the API Key |
auth_invalid_api_key | 401 | Invalid API Key | Verify that the API Key is correct |
invalid_category | 400 | category is not in the allow list | Use summary / medical / legal / all instead |
GET /api/v1/summary-templates/{slug} (Get a Single Summary Template's Details)
Exposes the full raw text of the built-in template's prompt definition, for reference when enterprise customers integrate. For the full schema, see reference/rest/summary-templates.md.
Authentication
Header: X-API-Key: YOUR_API_KEY
Request Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
slug | path | string | Yes | Template identifier |
Request Example
curl -X GET "https://vas-poc.vurbo.ai/api/v1/summary-templates/medical_consultation" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
Success Response
{
"data": {
"slug": "medical_consultation",
"name": "Medical Consultation",
"description": "Medical consultation record template",
"category": "medical",
"system_prompt": "You are a professional medical records specialist...",
"template_prompt": "[Task]\nGenerate a structured summary...",
"output_format": "[Summary Template Begin]\n## Patient Information\n..."
}
}
Error Responses
| Error Code | HTTP | Description |
|---|---|---|
template_not_found | 404 | The template with the specified slug does not exist or has been disabled (is_active=false) |
Glossary Validation API
POST /api/v1/glossary/validate (Validate a Glossary Before Saving)
Checks a glossary (terminology, fuzzy correction, translation dictionary) for format problems and internal conflicts before it is saved. Intended to be called from your glossary management UI before saving; it is not necessary to call it before every recording or audio import.
Most glossary problems produce no error message at all — they simply yield unexpected results, silently, during recording or translation. This endpoint surfaces problems of that kind at the moment the user presses Save, along with the index of each offending entry.
Note: The host is the realtime service domain (the same domain as
wss://, with the scheme swapped forhttps://), which may differ from the host of the other REST endpoints.
- Free: no points are deducted, and no task or recording is created
- Rate limited to 120 requests per minute per API Key, as a separate quota counted independently of the other REST endpoints
- The response carries no human-readable text; compose your own sentences from the conflict codes
curl -X POST "https://<realtime-host>/api/v1/glossary/validate" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW" \
-H "Content-Type: application/json" \
-d '{"fuzzy_correction":{"zh-TW":[{"correct":"報價","incorrect":["抱歉"]},{"correct":"抱歉"}]}}'
For the eight detectable conflicts, the full request/response specification, and every field definition, see the Glossary Validation API; for how to configure the glossary itself, see the Terminology Guide.
Error Handling
Unified Error Format
All API errors follow a unified format:
Simple format (external API):
{
"error_code": "auth_invalid_api_key",
"message": "Invalid API key"
}
Detailed format (internal API):
{
"type": "error",
"data": {
"error_code": "auth_invalid_api_key",
"severity": "fatal",
"message": "Invalid or expired API key",
"context": "auth",
"request_id": "req_abc123xyz789",
"timestamp": "2025-12-13T10:30:45.123Z",
"details": null
}
}
Error Code Overview
Authentication Errors
| error_code | HTTP Status | severity | Description |
|---|---|---|---|
auth_missing_api_key | 401 | fatal | API Key not provided |
auth_invalid_api_key | 401 | fatal | Invalid API Key |
auth_key_expired | 401 | fatal | API Key expired |
Resource Errors
| error_code | HTTP Status | Description |
|---|---|---|
recording_not_found | 404 | Recording does not exist |
recording_audio_not_ready | 422 | Audio file not ready |
Broadcast Errors
| error_code | HTTP Status | Description |
|---|---|---|
broadcast_not_found | 404 | Broadcast not found |
broadcast_session_ended | 410 | Broadcast ended |
broadcast_unauthorized | 403 | Unauthorized access |
Version: V1.24.1 Last Updated: 2026-10-07