Tasks
Endpoint Overview
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/tasks | Get task list |
| DELETE | /api/v1/tasks/{taskId} | Delete task |
| 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 |
| POST | /api/v1/tasks/{taskId}/force-fail | Manually force a task to be marked as failed |
| POST | /api/v1/tasks/{taskId}/retry | Reprocess a failed task |
| PUT | /api/v1/tasks/batch/pin | Batch update pin status |
| DELETE | /api/v1/tasks/batch | Batch delete tasks |
| GET | /api/v1/tasks/{taskId}/audio/export | Download task audio |
| GET | /api/v1/tasks/{taskId}/transcript/export | Download transcript (TXT / SRT / SBV / VTT / CSV) |
GET /api/v1/tasks
Description
Get the recording task list for the current user. Use the status parameter to filter tasks by processing stage.
Authentication
Header: X-API-Key (see Authentication)
Request Parameters
| Parameter | Location | Type | Required | Default | Description |
|---|---|---|---|---|---|
status | query | string | No | completed | Filter: completed (completed) / active (in progress) / all (all) |
status Filter Details
| Value | Tasks Returned | Description |
|---|---|---|
completed | processing_status = completed | Default value, backward compatible. Returns only completed tasks |
active | processing_status IN (recording, importing, uploading, processing) | Tasks in progress (live recording, audio import, uploading, processing) |
all | No filter | Returns all tasks (including in-progress and completed) |
Request Example
# Default: returns only completed tasks (backward compatible)
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"
Success Response
HTTP 200
{
"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,
"summary_mode": "custom",
"summary_template": "acme-meeting-v2",
"summary_language": "zh-TW"
}
]
}
}
Response Field Details
| 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 | null | Transcription language list (null when there is no transcription language) |
data.tasks[].translation_languages | array | null | Translation language list (null when there is no translation language, e.g. conversation mode or an import with no translation language set) |
data.tasks[].created_at | string | Creation time (ISO 8601) |
data.tasks[].processing_status | string | Processing status (see below) |
data.tasks[].is_pinned | boolean | Whether the task is pinned |
data.tasks[].is_unread | boolean | Whether the task is unread |
data.tasks[].summary_mode | string | null | Summary mode: "builtin" / "custom" / null (null when no summary has been generated) |
data.tasks[].summary_template | string | null | effective slug — builtin → built-in template slug; custom → customer slug (i.e. the prompt_slug from the request, pass-through for traceability) |
data.tasks[].summary_language | string | null | Summary output language |
processing_status Details
The processing status of a recording is updated in real time as processing progresses:
| Status | Description | Scenario |
|---|---|---|
recording | Live recording in progress | Live recording, broadcast |
importing | Audio import in progress | Audio import |
uploading | Uploading to cloud storage | After recording stops, after import completes |
pending | Pending (kept for backward compatibility) | Appears only in legacy data; newly created records never enter this status |
processing | Speech recognition and translation in progress | After upload completes |
completed | Processing complete | Final status |
failed | Processing failed | Final status |
Status transitions:
Live recording/broadcast: recording → uploading → processing → completed / failed
Audio import: importing → uploading → processing → completed / failed
About the
pendingstatus: Kept for backward compatibility with legacy data; newly created records never enter this status.force-failstill accepts records inpendingso that leftover historical data can be cleaned up.
Specific Error Codes
This endpoint has no specific error codes; it may only return common authentication errors.
DELETE /api/v1/tasks/{taskId}
Description
Delete the specified task (soft delete). After deletion, the task no longer appears in the list.
Authentication
Header: X-API-Key (see Authentication)
Request Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
taskId | path | 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
HTTP 200
{
"message": "Task deleted"
}
Response Field Details
| Field | Type | Description |
|---|---|---|
message | string | Operation result message |
Specific Error Codes
| Error Code | HTTP Status | Description | Suggested Action |
|---|---|---|---|
recording_not_found | 404 | Recording not found | Verify that taskId is correct |
recording_unauthorized | 403 | Unauthorized to access this recording | Verify that the task belongs to this user |
PUT /api/v1/tasks/{taskId}/pin
Description
Update the pin status of a task. Pinned tasks are shown first in the list.
Authentication
Header: X-API-Key (see Authentication)
Request Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
taskId | path | string | Yes | Task ID (UUID) |
is_pinned | body | 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
HTTP 200
{
"data": {
"is_pinned": true
}
}
Response Field Details
| Field | Type | Description |
|---|---|---|
data.is_pinned | boolean | Updated pin status |
Specific Error Codes
| Error Code | HTTP Status | Description | Suggested Action |
|---|---|---|---|
recording_not_found | 404 | Recording not found | Verify that taskId is correct |
validation_failed | 422 | Parameter validation failed | Verify that is_pinned is a boolean |
PUT /api/v1/tasks/{taskId}/read
Description
Mark a task as read.
Authentication
Header: X-API-Key (see Authentication)
Request Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
taskId | path | string | Yes | Task ID (UUID) |
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
HTTP 200
{
"data": {
"is_unread": false
}
}
Response Field Details
| Field | Type | Description |
|---|---|---|
data.is_unread | boolean | Updated unread status (always false) |
Specific Error Codes
| Error Code | HTTP Status | Description | Suggested Action |
|---|---|---|---|
recording_not_found | 404 | Recording not found | Verify that taskId is correct |
PATCH /api/v1/tasks/{taskId}/name
Description
Update the name of the specified task. The maximum name length is 60 characters (controlled by the RECORDING_NAME_MAX_LENGTH environment variable).
Authentication
Header: X-API-Key (see Authentication)
Request Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
taskId | path | string | Yes | Task ID (UUID) |
name | body | 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
HTTP 200
{
"message": "Recording name updated",
"data": {
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Product Meeting Discussion",
"name_source": "user"
}
}
Response Field Details
| Field | Type | Description |
|---|---|---|
message | string | Operation result message |
data.task_id | string | Task ID (UUID) |
data.name | string | Updated task name |
data.name_source | string | Name source: default (system default) / llm (AI generated) / user (user defined) |
Specific Error Codes
| Error Code | HTTP Status | Description | Suggested Action |
|---|---|---|---|
recording_not_found | 404 | Recording not found | Verify that taskId is correct |
recording_unauthorized | 403 | Unauthorized to access this recording | Verify that the task belongs to this user |
validation_failed | 422 | Validation failed | Verify that name is not empty and does not exceed 60 characters |
PUT /api/v1/tasks/batch/pin
Description
Batch update the pin status of multiple tasks. A single request can operate on up to 100 tasks. Only tasks belonging to the current user are affected; IDs that do not belong to this user are ignored.
Authentication
Header: X-API-Key (see Authentication)
Request Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
task_ids | body | array | Yes | Array of task IDs (each element is 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
HTTP 200
{
"data": {
"affected_count": 2
}
}
Response Field Details
| Field | Type | Description |
|---|---|---|
data.affected_count | number | Number of tasks actually updated |
Specific Error Codes
| Error Code | HTTP Status | Description | Suggested Action |
|---|---|---|---|
validation_failed | 422 | Parameter validation failed | Verify that task_ids is a UUID array of no more than 100 entries, and is_pinned is a boolean |
DELETE /api/v1/tasks/batch
Description
Batch delete multiple tasks (soft delete). A single request can operate on up to 100 tasks. Only tasks belonging to the current user are affected; IDs that do not belong to this user are ignored.
Authentication
Header: X-API-Key (see Authentication)
Request Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
task_ids | body | array | Yes | Array of task IDs (each element is 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
HTTP 200
{
"data": {
"affected_count": 2
}
}
Response Field Details
| Field | Type | Description |
|---|---|---|
data.affected_count | number | Number of tasks actually deleted |
Specific Error Codes
| Error Code | HTTP Status | Description | Suggested Action |
|---|---|---|---|
validation_failed | 422 | Parameter validation failed | Verify that task_ids is a UUID array of no more than 100 entries |
GET /api/v1/tasks/{taskId}/audio/export
Description
Download the original recording audio 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 filename uses the recording name (sanitized) when available; 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; the response does not force a download.
Authentication
Header: X-API-Key (see Authentication)
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: The vast majority of recordings are returned as an M4A container (AAC encoded), with
Content-Typeaudio/mp4and the.m4aextension. The actualContent-Typedepends on the recording's storage format (a few legacy / broadcast recordings may beaudio/webmoraudio/wav), so rely on the response header.
Specific Error Codes
| Error Code | HTTP Status | Description | Suggested Action |
|---|---|---|---|
recording_not_found | 404 | Recording 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 | Audio not yet uploaded or still processing | Retry later; you can 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 |
Frontend Example
async function downloadAudio(taskId, apiKey) {
const response = await fetch(
`https://vas-poc.vurbo.ai/api/v1/tasks/${taskId}/audio/export`,
{
headers: { 'X-API-Key': apiKey },
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Download failed: ${error.data?.message}`);
}
// Parse Content-Disposition to get the suggested filename
const disposition = response.headers.get('Content-Disposition') || '';
const match = disposition.match(/filename\*=UTF-8''([^;]+)/);
const filename = match ? decodeURIComponent(match[1]) : `audio-${taskId}`;
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
GET /api/v1/tasks/{taskId}/transcript/export
Description
Download the transcript of the specified task in one of five formats: plain text, SubRip subtitles, YouTube SBV subtitles, WebVTT subtitles, or a CSV spreadsheet. The response content includes the original text and all translation languages; it carries a Content-Disposition: attachment header for direct download. The filename uses the recording name (sanitized) when available; 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 in one response; can be opened directly in subtitle software or a spreadsheet.
- SSE history API: for progressive loading; pushes the raw structured data (JSON fragments) sentence by sentence as an event stream, for progressive rendering in a frontend UI.
Authentication
Header: X-API-Key (see Authentication)
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 Details
| Format | Time Format | Content Structure | Typical Use |
|---|---|---|---|
txt | — | One line per segment, [Speaker] original text, with translations indented by 4 spaces as [language code] translated text | Reading, recordkeeping |
srt | HH:MM:SS,mmm | Includes sequence numbers; after each timecode, the original text and translations each occupy one line | SubRip subtitles (DaVinci Resolve, VLC, etc.) |
sbv | H:MM:SS.mmm | No sequence numbers; timecodes separated by ,; original text and translations joined into a single line with | (line breaks are replaced with spaces) | YouTube subtitle upload |
vtt | HH:MM:SS.mmm | Uses WEBVTT as the header; after each timecode, the original text and translations each occupy one line | HTML5 <track> subtitles, web players |
csv | HH:MM:SS (no milliseconds) | Starts 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 (opens directly in Excel; the UTF-8 BOM keeps non-ASCII text from becoming 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
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: 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 by theformatparameter:
Format Content-Type txttext/plain; charset=UTF-8srtapplication/x-subripsbvtext/plain; charset=UTF-8vtttext/vtt; charset=UTF-8csvtext/csv; charset=UTF-8
Output Examples
Assume the transcript contains two English recording segments (en-US) with 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,多謝,ありがとう
Specific Error Codes
| Error Code | HTTP Status | Description | Suggested Action |
|---|---|---|---|
recording_not_found | 404 | Recording not found | Verify that taskId is correct and the recording has not been deleted |
recording_transcript_not_ready | 422 | Transcript not yet generated or empty | First confirm via GET /api/v1/tasks that processing_status = completed before calling |
validation_failed | 422 | Parameter validation failed | Verify that 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 |
Frontend Example
async function downloadTranscript(taskId, apiKey, format = 'srt') {
const response = await fetch(
`https://vas-poc.vurbo.ai/api/v1/tasks/${taskId}/transcript/export?format=${format}`,
{
headers: { 'X-API-Key': apiKey },
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Download failed: ${error.data?.message}`);
}
// Parse Content-Disposition to get the suggested filename
const disposition = response.headers.get('Content-Disposition') || '';
const match = disposition.match(/filename\*=UTF-8''([^;]+)/);
const filename = match
? decodeURIComponent(match[1])
: `transcript-${taskId}.${format}`;
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
POST /api/v1/tasks/{taskId}/force-fail
Description
Force-mark as failed a task that is stuck in a non-terminal state (recording / importing / uploading / pending / processing). Use case: the upstream WebSocket connection broke abnormally but the follow-up status notification never arrived, or the user wants to abandon a recording that is still uploading and does not want to wait for the CheckStaleRecordings schedule (up to 30 minutes) to clean it up.
After a successful operation:
processing_statusbecomesfailed, andprocessing_erroris written with the reason provided by the user- A
recording.failedwebhook is triggered, withpayload.failure_sourceset touser_forced, so subscribers can distinguish this source from "job execution failure" and "automatic stale scan" - When the backend later pushes
uploading/processingto the internal API, it receives a 409, so the terminal-state protection takes effect
Note: Calling this endpoint on a task that is already in a terminal state (
completed/failed) returns 422. To remove a completed task, useDELETE /api/v1/tasks/{taskId}instead.
Authentication
Header: X-API-Key (see Authentication)
Request Parameters
| Parameter | Location | Type | Required | Default | Description |
|---|---|---|---|---|---|
taskId | path | string | Yes | — | Task ID (UUID) |
reason | body | string | null | No | null | Failure reason description, up to 500 characters. When empty, the system automatically generates User-forced failure (previous status: xxx) |
Request Example
# Without a reason
curl -X POST "https://vas-poc.vurbo.ai/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000/force-fail" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
# With a reason
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": "Recording client disconnected too long, giving up"}'
Success Response
HTTP 200
{
"data": {
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"processing_status": "failed",
"processing_error": "User-forced failure: Recording client disconnected too long, giving up (previous status: recording)"
}
}
Response Field Details
| Field | Type | Description |
|---|---|---|
data.task_id | string | Task ID (UUID) |
data.processing_status | string | Always failed after a forced failure |
data.processing_error | string | The complete failure reason written to the DB (including the previous status) |
Specific Error Codes
| Error Code | HTTP Status | Description | Suggested Action |
|---|---|---|---|
recording_not_found | 404 | Recording not found, or the recording does not belong to this user | Verify that taskId is correct and the recording has not been deleted |
invalid_processing_status | 422 | The task is already in a terminal state (completed / failed) and cannot be forced to fail | For completed tasks, use DELETE instead; failed tasks do not need to be force-failed again |
validation_failed | 422 | reason exceeds 500 characters, or taskId has an invalid format | Check the length of reason and the UUID format of taskId |
Frontend Example
async function forceFailTask(taskId, apiKey, reason = null) {
const body = reason ? JSON.stringify({ reason }) : undefined;
const response = await fetch(
`https://vas-poc.vurbo.ai/api/v1/tasks/${taskId}/force-fail`,
{
method: 'POST',
headers: {
'X-API-Key': apiKey,
'Content-Type': 'application/json',
},
body,
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Force-fail failed: ${error.data?.message}`);
}
return await response.json();
}
POST /api/v1/tasks/{taskId}/retry
Description
Re-queue for processing (ProcessRecordingJob) a task that is in the failed state. This is for cases where a transient backend service failure (such as the LLM being rate limited or the summary service briefly going down) caused the job to fail and the user wants to retry.
Preconditions
| Condition | Required Value |
|---|---|
processing_status | failed |
audio_status | success |
transcript_status | success |
If any condition is not met, 422 invalid_processing_status is returned. If the audio or transcript has not finished uploading, re-dispatching would immediately fail again (because the processor cannot find the source), so this endpoint blocks it in advance to avoid a retry loop.
After a successful operation:
processing_statusbecomesprocessing, andprocessing_erroris clearedProcessRecordingJobis dispatched to therecordingsqueue inafterCommit()mode, ensuring the queue worker does not read the old value before the DB transaction commits- No webhook is triggered (
recording.completed/recording.failedis triggered only when the job finishes)
Authentication
Header: X-API-Key (see Authentication)
Request Parameters
| Parameter | Location | Type | Required | Default | 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"
}
}
Specific Error Codes
| Error Code | HTTP Status | Description | details Field | Suggested Action |
|---|---|---|---|---|
recording_not_found | 404 | Recording not found, or the recording does not belong to this user | — | Verify that taskId is correct |
invalid_processing_status | 422 | The task is not in the failed state | details.current_status | Only failed tasks can be retried |
invalid_processing_status | 422 | Audio / transcript not fully uploaded | details.audio_status, details.transcript_status | First confirm whether the source is complete; if the recording source is already corrupted, use force-fail to close it out and re-record |
Frontend Example
async function retryTask(taskId, apiKey) {
const response = await fetch(
`https://vas-poc.vurbo.ai/api/v1/tasks/${taskId}/retry`,
{
method: 'POST',
headers: { 'X-API-Key': apiKey },
}
);
if (!response.ok) {
const error = await response.json();
const details = error.data?.details;
if (details?.audio_status && details.audio_status !== 'success') {
throw new Error(`Cannot retry: audio not ready (${details.audio_status})`);
}
throw new Error(`Retry failed: ${error.data?.message}`);
}
return await response.json();
}
Version: V1.5.7 Last Updated: 2026-05-20