Import Progress
Connection Information
| Item | Value |
|---|---|
| Base Path | https://vas-poc.vurbo.ai/api/v1/sse |
| Protocol | HTTP + Server-Sent Events (SSE) |
| Data Format | text/event-stream |
| Authentication | Header X-API-Key: {KEY} |
Note: The browser's native EventSource API does not support custom headers. Use the fetch API together with ReadableStream, or use an SSE client library that supports headers.
Endpoint Overview
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/sse/imports/{importId}/progress | Track audio import progress in real time |
GET /api/v1/sse/imports/{importId}/progress
Description
Track the processing progress of an audio import in real time. After the connection is established, progress updates are continuously pushed over the SSE stream until the import completes, fails, or the connection times out.
Use Cases
- Display a real-time progress bar after uploading an audio file
- Track the progress of each stage: audio conversion, transcription, translation, and summary
Authentication
Header: X-API-Key (see Authentication)
Request Parameters
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
importId | path | string | Yes | Import task ID (UUID) |
Request Example
curl -N "https://vas-poc.vurbo.ai/api/v1/sse/imports/550e8400-e29b-41d4-a716-446655440000/progress" \
-H "X-API-Key: vas_aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW"
// Use the fetch API (because EventSource does not support headers)
async function connectSSE(importId, apiKey) {
const response = await fetch(
`https://vas-poc.vurbo.ai/api/v1/sse/imports/${importId}/progress`,
{
headers: {
'X-API-Key': apiKey
}
}
);
const reader = response.body.getReader();
// ... handle SSE events
}
Event Sequence
Scenario 1: Import still in progress
┌────────────────────────────────────────────────────┐
│ 1. connected → Connection confirmed │
│ 2. progress → Send current progress │
│ 3. progress ×N → Pushed continuously on change │
│ heartbeat ×N → Sent every 15s if no change │
│ 4. completed → Import succeeded, connection ends │
│ or failed → Import failed, connection ends │
│ or timeout → Exceeded 15 minutes, connection ends │
└────────────────────────────────────────────────────┘
Scenario 2: Import already completed (terminal state)
┌────────────────────────────────────────────────────┐
│ 1. connected → Connection confirmed │
│ 2. progress → Send final progress │
│ 3. completed → Send completed event directly and end │
│ or failed → Send failed event directly and end │
└────────────────────────────────────────────────────┘
Event Formats
connected
Connection success confirmation.
event: connected
data: {"message":"Import progress service connected (importId: 550e8400-e29b-41d4-a716-446655440000)"}
{
"message": "Import progress service connected (importId: xxx)"
}
| Field | Type | Description |
|---|---|---|
message | string | Connection confirmation message |
progress
Processing progress update. Sent whenever the progress percentage changes.
event: progress
data: {"import_id":"550e8400-...","status":"processing","stage":"transcribing","progress":45,"message":"Transcribing..."}
{
"import_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing",
"stage": "transcribing",
"progress": 45,
"message": "Transcribing..."
}
| Field | Type | Description |
|---|---|---|
import_id | string | Import task ID (UUID) |
status | string | Import status, see "Status and Stage" below |
stage | string | null | Current processing stage, see "Status and Stage" below |
progress | integer | Progress percentage (0-100) |
message | string | Human-readable progress message |
Status (status) values:
| Value | Description |
|---|---|
pending | Waiting to be processed |
processing | Processing |
completed | Completed |
failed | Failed |
Stage (stage) values and corresponding progress ranges:
| Value | Description | Progress Range |
|---|---|---|
converting | Audio format conversion | 0% - 10% |
transcribing | Speech-to-text | 10% - 60% |
translating | Text translation | 60% - 85% |
summarizing | Summary generation | 85% - 100% |
null | Not yet started (pending status) | — |
completed
Import processing completed. The connection closes automatically after this event is received.
event: completed
data: {"import_id":"550e8400-...","status":"completed","task_id":"abc123-...","message":"Processing completed"}
{
"import_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"task_id": "abc123-e29b-41d4-a716-446655440000",
"message": "Processing completed"
}
| Field | Type | Description |
|---|---|---|
import_id | string | Import task ID (UUID) |
status | string | Always completed |
task_id | string | The generated recording ID (recording_id), which can be used for subsequent queries |
message | string | Always Processing completed |
Edge case (v1.3.5): If no speech content can be recognized in the audio—because of silence, low volume, noise, or a mismatch between the recognition language and the audio—the import still ends with a
completedevent (notfailed), andtask_idis produced normally. However, the transcript later loaded viaGET /api/v1/sse/history/transcribe/{taskId}has a sentence count of0. Clients should use the sentence count to decide whether to show an empty "no speech content" state, rather than treating this as an error. See File Import Guide – Behavior When Audio Cannot Be Recognized.
failed
Import processing failed. The connection closes automatically after this event is received.
event: failed
data: {"import_id":"550e8400-...","status":"failed","error_code":"import_invalid_format","error_message":"Unsupported audio format"}
{
"import_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error_code": "import_invalid_format",
"error_message": "Unsupported audio format"
}
| Field | Type | Description |
|---|---|---|
import_id | string | Import task ID (UUID) |
status | string | Always failed |
error_code | string | Error code |
error_message | string | Human-readable error message |
heartbeat
Heartbeat event. Sent every 15 seconds when there is no progress change, to keep the connection alive.
event: heartbeat
data: {"timestamp":1708761600}
{
"timestamp": 1708761600
}
| Field | Type | Description |
|---|---|---|
timestamp | integer | Unix timestamp |
timeout
Connection timeout event. Sent when the import does not complete within 15 minutes; the connection then closes automatically.
event: timeout
data: {"message":"Connection timed out"}
{
"message": "Connection timed out"
}
| Field | Type | Description |
|---|---|---|
message | string | Always Connection timed out |
Specific Error Codes
After the connection is established, if the import task does not exist, it is returned via an error event:
| Error Code | Description | Recommended Action |
|---|---|---|
import_not_found | The specified import task was not found | Verify that the importId is correct |
Frontend Example
async function trackImportProgress(importId, apiKey) {
const response = await fetch(
`https://vas-poc.vurbo.ai/api/v1/sse/imports/${importId}/progress`,
{
headers: {
'X-API-Key': apiKey
}
}
);
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// Parse SSE format: event: xxx\ndata: {...}\n\n
const events = buffer.split('\n\n');
buffer = events.pop(); // Keep the incomplete part
for (const eventStr of events) {
if (!eventStr.trim()) continue;
const lines = eventStr.split('\n');
let eventType = '';
let eventData = '';
for (const line of lines) {
if (line.startsWith('event: ')) {
eventType = line.slice(7);
} else if (line.startsWith('data: ')) {
eventData = line.slice(6);
}
}
if (!eventType || !eventData) continue;
const data = JSON.parse(eventData);
switch (eventType) {
case 'connected':
console.log('Connected:', data.message);
break;
case 'progress':
console.log(`[${data.stage}] ${data.progress}% - ${data.message}`);
// Update the UI progress bar
updateProgressBar(data.progress, data.stage, data.message);
break;
case 'completed':
console.log('Import completed! Recording ID:', data.task_id);
// Navigate to the recording details page
navigateToRecording(data.task_id);
break;
case 'failed':
console.error('Import failed:', data.error_code, data.error_message);
// Show the error message
showError(data.error_message);
break;
case 'heartbeat':
console.log('Heartbeat:', new Date(data.timestamp * 1000));
break;
case 'timeout':
console.warn('Connection timed out:', data.message);
break;
case 'error':
console.error('Error:', data);
break;
}
}
}
}
Version: V1.5.7 Last Updated: 2026-05-20