curl -X POST https://api.bota.dev/v1/recordings/rec_abc123/upload-complete \
-H "Authorization: Bearer sk_live_..."
curl -X POST https://api.bota.dev/v1/recordings/rec_abc123/upload-complete \
-H "Authorization: Bearer dtok_..."
const response = await fetch('https://api.bota.dev/v1/recordings/rec_abc123/upload-complete', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_live_...',
},
});
const recording = await response.json();
console.log(recording.status); // 'uploaded'
import requests
response = requests.post(
'https://api.bota.dev/v1/recordings/rec_abc123/upload-complete',
headers={'Authorization': 'Bearer sk_live_...'},
)
recording = response.json()
print(recording['status']) # 'uploaded'
{
"id": "rec_abc123",
"device_id": "dev_abc123",
"end_user_id": "eu_xyz789",
"name": "Product interview - Jane Doe",
"status": "uploaded",
"duration_ms": 1800000,
"started_at": "2025-01-15T09:00:00Z",
"ended_at": "2025-01-15T09:30:00Z",
"transcription_id": null,
"media": [
{
"id": "med_001",
"type": "audio",
"mime_type": "audio/opus",
"file_size_bytes": 2048576,
"status": "uploaded",
"captured_at": "2025-01-15T09:00:00Z",
"created_at": "2025-01-15T10:00:00Z"
},
{
"id": "med_002",
"type": "image",
"mime_type": "image/jpeg",
"file_size_bytes": 245000,
"status": "uploaded",
"captured_at": "2025-01-15T09:05:30Z",
"created_at": "2025-01-15T10:01:00Z"
}
],
"metadata": {
"meeting_type": "interview"
},
"created_at": "2025-01-15T10:00:00Z"
}
{
"error": {
"code": "invalid_state",
"message": "Recording is not in pending state"
}
}
{
"error": {
"code": "not_found",
"message": "Recording not found"
}
}
Uploads
Complete Upload
Mark the recording upload as complete
POST
/
recordings
/
{id}
/
upload-complete
curl -X POST https://api.bota.dev/v1/recordings/rec_abc123/upload-complete \
-H "Authorization: Bearer sk_live_..."
curl -X POST https://api.bota.dev/v1/recordings/rec_abc123/upload-complete \
-H "Authorization: Bearer dtok_..."
const response = await fetch('https://api.bota.dev/v1/recordings/rec_abc123/upload-complete', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_live_...',
},
});
const recording = await response.json();
console.log(recording.status); // 'uploaded'
import requests
response = requests.post(
'https://api.bota.dev/v1/recordings/rec_abc123/upload-complete',
headers={'Authorization': 'Bearer sk_live_...'},
)
recording = response.json()
print(recording['status']) # 'uploaded'
{
"id": "rec_abc123",
"device_id": "dev_abc123",
"end_user_id": "eu_xyz789",
"name": "Product interview - Jane Doe",
"status": "uploaded",
"duration_ms": 1800000,
"started_at": "2025-01-15T09:00:00Z",
"ended_at": "2025-01-15T09:30:00Z",
"transcription_id": null,
"media": [
{
"id": "med_001",
"type": "audio",
"mime_type": "audio/opus",
"file_size_bytes": 2048576,
"status": "uploaded",
"captured_at": "2025-01-15T09:00:00Z",
"created_at": "2025-01-15T10:00:00Z"
},
{
"id": "med_002",
"type": "image",
"mime_type": "image/jpeg",
"file_size_bytes": 245000,
"status": "uploaded",
"captured_at": "2025-01-15T09:05:30Z",
"created_at": "2025-01-15T10:01:00Z"
}
],
"metadata": {
"meeting_type": "interview"
},
"created_at": "2025-01-15T10:00:00Z"
}
{
"error": {
"code": "invalid_state",
"message": "Recording is not in pending state"
}
}
{
"error": {
"code": "not_found",
"message": "Recording not found"
}
}
Mark the recording upload as complete after all files have been uploaded to S3. This transitions the recording status from
pending to uploaded and fires an event for downstream processing (e.g., transcription).
Authentication
This endpoint accepts two authentication methods:| Auth | Use case |
|---|---|
| API key | Backend uploading on behalf of devices (BLE sync) |
| Device token | 4G/WiFi devices uploading directly |
curl -X POST https://api.bota.dev/v1/recordings/rec_abc123/upload-complete \
-H "Authorization: Bearer sk_live_..."
curl -X POST https://api.bota.dev/v1/recordings/rec_abc123/upload-complete \
-H "Authorization: Bearer dtok_..."
const response = await fetch('https://api.bota.dev/v1/recordings/rec_abc123/upload-complete', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_live_...',
},
});
const recording = await response.json();
console.log(recording.status); // 'uploaded'
import requests
response = requests.post(
'https://api.bota.dev/v1/recordings/rec_abc123/upload-complete',
headers={'Authorization': 'Bearer sk_live_...'},
)
recording = response.json()
print(recording['status']) # 'uploaded'
Path Parameters
The recording’s unique identifier (e.g.,
rec_abc123).Response
Returns the updated recording object with status set touploaded.
{
"id": "rec_abc123",
"device_id": "dev_abc123",
"end_user_id": "eu_xyz789",
"name": "Product interview - Jane Doe",
"status": "uploaded",
"duration_ms": 1800000,
"started_at": "2025-01-15T09:00:00Z",
"ended_at": "2025-01-15T09:30:00Z",
"transcription_id": null,
"media": [
{
"id": "med_001",
"type": "audio",
"mime_type": "audio/opus",
"file_size_bytes": 2048576,
"status": "uploaded",
"captured_at": "2025-01-15T09:00:00Z",
"created_at": "2025-01-15T10:00:00Z"
},
{
"id": "med_002",
"type": "image",
"mime_type": "image/jpeg",
"file_size_bytes": 245000,
"status": "uploaded",
"captured_at": "2025-01-15T09:05:30Z",
"created_at": "2025-01-15T10:01:00Z"
}
],
"metadata": {
"meeting_type": "interview"
},
"created_at": "2025-01-15T10:00:00Z"
}
{
"error": {
"code": "invalid_state",
"message": "Recording is not in pending state"
}
}
{
"error": {
"code": "not_found",
"message": "Recording not found"
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
id | string | The recording’s unique identifier |
device_id | string | The device that created this recording |
end_user_id | string | The end user associated with this recording |
name | string | null | Human-readable name for the recording |
status | string | Recording status (will be uploaded on success) |
duration_ms | integer | null | Duration of the recording in milliseconds |
started_at | string | ISO 8601 timestamp when the recording started |
ended_at | string | ISO 8601 timestamp when the recording ended |
transcription_id | string | null | Associated transcription ID, if any |
media | array | Files associated with this recording |
media[].id | string | Media identifier (med_*) |
media[].type | string | Media type: audio, image, or video |
media[].mime_type | string | MIME type of the file |
media[].file_size_bytes | integer | File size in bytes |
media[].status | string | Upload status: pending, uploaded |
media[].captured_at | string | null | ISO 8601 timestamp of when the media was captured |
media[].created_at | string | ISO 8601 timestamp of when the media record was created |
metadata | object | Custom metadata attached to the recording |
created_at | string | ISO 8601 timestamp when the recording was created |
Complete Upload Flow
// 1. Create recording
const recording = await createRecording({ device_id: 'dev_abc123' });
// 2. Upload audio
const audio = await getUploadUrl(recording.id, { type: 'audio', content_type: 'audio/opus' });
await uploadToS3(audio.upload_url, audioBuffer, 'audio/opus');
// 3. Upload images (optional)
const image = await getUploadUrl(recording.id, {
type: 'image',
content_type: 'image/jpeg',
captured_at: '2025-01-15T09:05:30Z',
});
await uploadToS3(image.upload_url, imageBuffer, 'image/jpeg');
// 4. Mark complete — fires event for downstream processing
const updated = await completeUpload(recording.id);
console.log(updated.status); // 'uploaded'
The recording must be in
pending status. Calling this on an already-uploaded recording returns a 400 error.Was this page helpful?
⌘I

