REST API

Tasks API

Endpoint Overview

MethodEndpointDescription
GET/api/v1/tasksGet task list
DELETE/api/v1/tasks/{taskId}Delete task
PUT/api/v1/tasks/{taskId}/pinUpdate pin status
PUT/api/v1/tasks/{taskId}/readMark as read
PATCH/api/v1/tasks/{taskId}/nameUpdate task name
POST/api/v1/tasks/{taskId}/force-failManually force a task to be marked as failed
POST/api/v1/tasks/{taskId}/retryReprocess a failed task
PUT/api/v1/tasks/batch/pinBatch update pin status
DELETE/api/v1/tasks/batchBatch delete tasks
GET/api/v1/tasks/{taskId}/audio/exportDownload task audio
GET/api/v1/tasks/{taskId}/transcript/exportDownload 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, or task_ids[] to query specific tasks only.

Authentication

Header: X-API-Key (see Authentication)

Request Parameters

ParameterLocationTypeRequiredDefaultDescription
statusquerystringNocompletedFilter: completed (completed) / active (in progress) / all (all)
task_ids[]querystring[]No—Query these tasks only (each element is a UUID, 1–100 entries)

status Filter Details

ValueTasks ReturnedDescription
completedprocessing_status = completedDefault value, backward compatible. Returns only completed tasks
activeprocessing_status IN (recording, importing, uploading, processing)Tasks in progress (live recording, audio import, uploading, processing)
allNo filterReturns all tasks (including in-progress and completed)

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.
  • status still applies: without status, only completed tasks are returned. To look up the listed tasks regardless of status, add status=all.
  • The response format is the same as without task_ids.
  • More than 100 entries, an entry that is not a UUID, or task_ids that is not an array (for example, task_ids=<UUID> without []) returns 422 validation_failed.

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"

# 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

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

FieldTypeDescription
data.tasksarrayTask list
data.tasks[].task_idstringTask ID (UUID)
data.tasks[].titlestringTask title
data.tasks[].typestringRecording type
data.tasks[].type_sourcestringSource type (realtime / import / broadcast)
data.tasks[].duration_msnumberRecording duration (milliseconds)
data.tasks[].duration_formattedstringFormatted duration (min:sec)
data.tasks[].transcription_languagesarray | nullTranscription language list (null when there is no transcription language)
data.tasks[].translation_languagesarray | nullTranslation language list (null when there is no translation language, e.g. conversation mode or an import with no translation language set)
data.tasks[].created_atstringCreation time (ISO 8601)
data.tasks[].processing_statusstringProcessing status (see below)
data.tasks[].is_pinnedbooleanWhether the task is pinned
data.tasks[].is_unreadbooleanWhether the task is unread
data.tasks[].summary_modestring | nullSummary mode: "builtin" / "custom" / null (null when no summary has been generated)
data.tasks[].summary_templatestring | nulleffective slug — builtin → built-in template slug; custom → customer slug (i.e. the prompt_slug from the request, pass-through for traceability)
data.tasks[].summary_languagestring | nullSummary language. If no summary language was specified for the recording, this is usually the first transcription language; in two-way mode with active_language specified, it is that language. It may be null for tasks without a summary (same as summary_language in the history init_metadata)

processing_status Details

The processing status of a recording is updated in real time as processing progresses:

StatusDescriptionScenario
recordingLive recording in progressLive recording, broadcast
importingAudio import in progressAudio import
uploadingUploading to cloud storageAfter recording stops, after import completes
pendingPending (kept for backward compatibility)Appears only in legacy data; newly created records never enter this status
processingSpeech recognition and translation in progressAfter upload completes
completedProcessing completeFinal status
failedProcessing failedFinal status

Status transitions:

Live recording/broadcast: recording → uploading → processing → completed / failed
Audio import:             importing → uploading → processing → completed / failed

About the pending status: Kept for backward compatibility with legacy data; newly created records never enter this status. force-fail still accepts records in pending so that leftover historical data can be cleaned up.

Specific Error Codes

Error CodeHTTP StatusDescriptionSuggested Action
validation_failed422Parameter validation failedVerify that task_ids is a UUID array of 1–100 entries

DELETE /api/v1/tasks/{taskId}

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.

Authentication

Header: X-API-Key (see Authentication)

Request Parameters

ParameterLocationTypeRequiredDescription
taskIdpathstringYesTask 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

FieldTypeDescription
messagestringOperation result message

Specific Error Codes

Error CodeHTTP StatusDescriptionSuggested Action
recording_not_found404Recording not foundVerify that taskId is correct
invalid_processing_status422The task is still being processed and cannot be deletedWait 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/{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

ParameterLocationTypeRequiredDescription
taskIdpathstringYesTask ID (UUID)
is_pinnedbodybooleanYesPin 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

FieldTypeDescription
data.is_pinnedbooleanUpdated pin status

Specific Error Codes

Error CodeHTTP StatusDescriptionSuggested Action
recording_not_found404Recording not foundVerify that taskId is correct
validation_failed422Parameter validation failedVerify 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

ParameterLocationTypeRequiredDescription
taskIdpathstringYesTask 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

FieldTypeDescription
data.is_unreadbooleanUpdated unread status (always false)

Specific Error Codes

Error CodeHTTP StatusDescriptionSuggested Action
recording_not_found404Recording not foundVerify 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 (configured on the server side).

Authentication

Header: X-API-Key (see Authentication)

Request Parameters

ParameterLocationTypeRequiredDescription
taskIdpathstringYesTask ID (UUID)
namebodystringYesTask 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

FieldTypeDescription
messagestringOperation result message
data.task_idstringTask ID (UUID)
data.namestringUpdated task name
data.name_sourcestringName source: default (system default) / llm (AI generated) / user (user defined)

Specific Error Codes

Error CodeHTTP StatusDescriptionSuggested Action
not_found404The recording has been deletedA taskId that simply does not exist is rejected earlier with 422 validation_failed; only deleted tasks reach this path
recording_unauthorized403Unauthorized to access this recordingVerify that the task belongs to this user
validation_failed422Validation failedVerify 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

ParameterLocationTypeRequiredDescription
task_idsbodyarrayYesArray of task IDs (each element is a UUID, up to 100)
is_pinnedbodybooleanYesPin 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

FieldTypeDescription
data.affected_countnumberNumber of tasks actually updated

Specific Error Codes

Error CodeHTTP StatusDescriptionSuggested Action
validation_failed422Parameter validation failedVerify 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. Deletion is permanent and cannot be undone, with the same scope as single-task deletion. 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.

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 (see Authentication)

Request Parameters

ParameterLocationTypeRequiredDescription
task_idsbodyarrayYesArray 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,
    "skipped_task_ids": []
  }
}

Response Field Details

FieldTypeDescription
data.affected_countnumberNumber of tasks actually deleted
data.skipped_task_idsstring[]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

Specific Error Codes

Error CodeHTTP StatusDescriptionSuggested Action
validation_failed422Parameter validation failedVerify 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: attachment header; 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

ParameterLocationTypeRequiredDescription
taskIdpathstringYesTask 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 -OJ lets curl automatically name the saved file based on the server's Content-Disposition response.

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-Type audio/mp4 and the .m4a extension. The actual Content-Type depends on the recording's storage format (a few legacy / broadcast recordings may be audio/webm or audio/wav), so rely on the response header.

Specific Error Codes

Error CodeHTTP StatusDescriptionSuggested Action
recording_not_found404Recording not found, or the audio file does not exist in cloud storageVerify that taskId is correct and the recording has not been deleted
recording_audio_not_ready422Audio not yet uploaded or still processingRetry later; you can first confirm via GET /api/v1/tasks that processing_status is completed
storage_download_failed500Storage service download failedRetry 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

ParameterLocationTypeRequiredDefaultDescription
taskIdpathstringYes—Task ID (UUID)
formatquerystringNotxtFormat: txt / srt / sbv / vtt / csv

format Details

FormatTime FormatContent StructureTypical Use
txt—One line per segment, [Speaker] original text, with translations indented by 4 spaces as [language code] translated textReading, recordkeeping
srtHH:MM:SS,mmmIncludes sequence numbers; after each timecode, the original text and translations each occupy one lineSubRip subtitles (DaVinci Resolve, VLC, etc.)
sbvH:MM:SS.mmmNo sequence numbers; timecodes separated by ,; original text and translations joined into a single line with | (line breaks are replaced with spaces)YouTube subtitle upload
vttHH:MM:SS.mmmUses WEBVTT as the header; after each timecode, the original text and translations each occupy one lineHTML5 <track> subtitles, web players
csvHH:MM:SS (no milliseconds)Starts with a UTF-8 BOM; columns index,start,end,speaker,text,<one column per translation language>. A language with no usable translations anywhere in the transcript gets no columnExcel, 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 -OJ lets curl automatically name the saved file based on the server's Content-Disposition response.

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-Type is determined dynamically by the format parameter:

FormatContent-Type
txttext/plain; charset=UTF-8
srtapplication/x-subrip
sbvtext/plain; charset=UTF-8
vtttext/vtt; charset=UTF-8
csvtext/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 CodeHTTP StatusDescriptionSuggested Action
recording_not_found404Recording not foundVerify that taskId is correct and the recording has not been deleted
recording_transcript_not_ready422Transcript not yet generated or emptyFirst confirm via GET /api/v1/tasks that processing_status = completed before calling
validation_failed422Parameter validation failedVerify that format is one of the allowed values (txt / srt / sbv / vtt / csv)
storage_download_failed500Storage service download failedRetry 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 live 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 automatic cleanup of timed-out tasks.

After a successful operation:

  • processing_status becomes failed, and processing_error is written with the reason provided by the user
  • A recording.failed webhook is triggered, with payload.failure_source set to user_forced, so subscribers can distinguish this source from "system processing failure" and "automatic timeout cleanup"
  • If uploading / processing is pushed for this task afterwards, it is rejected with 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, use DELETE /api/v1/tasks/{taskId} instead.

Authentication

Header: X-API-Key (see Authentication)

Request Parameters

ParameterLocationTypeRequiredDefaultDescription
taskIdpathstringYes—Task ID (UUID)
reasonbodystring | nullNonullFailure 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

FieldTypeDescription
data.task_idstringTask ID (UUID)
data.processing_statusstringAlways failed after a forced failure
data.processing_errorstringThe complete failure reason written to the DB (including the previous status)

Specific Error Codes

Error CodeHTTP StatusDescriptionSuggested Action
recording_not_found404Recording not found, or the recording does not belong to this userVerify that taskId is correct and the recording has not been deleted
invalid_processing_status422The task is already in a terminal state (completed / failed) and cannot be forced to failFor completed tasks, use DELETE instead; failed tasks do not need to be force-failed again
validation_failed422reason exceeds 500 characters, or taskId has an invalid formatCheck 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 a task that is in the failed state. This is for cases where a transient backend service failure (such as the LLM service being rate limited or the summary service briefly going down) caused processing to fail and the user wants to retry.

Preconditions

ConditionRequired Value
processing_statusfailed
audio_statussuccess
transcript_statussuccess

If any condition is not met, 422 invalid_processing_status is returned. If the audio or transcript has not finished uploading, the retry would immediately fail again (because the source cannot be found), so this endpoint blocks it in advance to avoid a pointless retry loop.

Even when every prerequisite is met, if another processing run for the same task has not finished yet, 409 task_already_processing is returned and the task status is left unchanged; send the same request again later.

Note: After a task fails, the system retries it automatically a few times. During that period processing_status may already read failed while a retry still returns 409 — this means the automatic retries have not finished; send the same request again a few minutes later.

After a successful operation:

  • processing_status becomes processing, and processing_error is cleared
  • The task is re-queued for processing; the platform guarantees the processor sees the updated status and never reruns against the previous one
  • No webhook is triggered (recording.completed / recording.failed is triggered only when the job finishes)

Authentication

Header: X-API-Key (see Authentication)

Request Parameters

ParameterLocationTypeRequiredDefaultDescription
taskIdpathstringYes—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 CodeHTTP StatusDescriptiondetails FieldSuggested Action
recording_not_found404Recording not found, or the recording does not belong to this user—Verify that taskId is correct
invalid_processing_status422The task is not in the failed statedetails.current_statusOnly failed tasks can be retried
invalid_processing_status422Audio / transcript not fully uploadeddetails.audio_status, details.transcript_statusFirst confirm whether the source is complete; if the recording source is already corrupted, use force-fail to close it out and re-record
task_already_processing409Another processing run for the same task has not finished yetdetails.task_idSend the same request again later; no parameter changes are needed

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.24.1 Last Updated: 2026-09-29

Copyright © 2026