Skip to main content
For videos larger than 100MB, use the resumable upload flow. This two-step process generates an upload URL, then your client uploads directly using the TUS protocol.

Step 1: Create Upload URL

POST /v1/gallery/upload/direct

Request Body

fileName
string
Desired filename for the video.
folderId
string
Destination folder ID.
maxDurationSeconds
integer
Maximum video duration in seconds. Default: 21600 (6 hours).
expiryMinutes
integer
How long the upload URL remains valid. Default: 30, max: 360.
metadata
object
Custom metadata to attach to the video.

Headers

Authorization
string
required
Bearer token (e.g. Authorization: Bearer YOUR_API_KEY)

Response

uploadUrl
string
The TUS upload URL. Use this with a TUS client to upload the video.
fileId
string
The file ID. Use this to check upload status.
expiresAt
string
ISO timestamp when the upload URL expires.
tusVersion
string
TUS protocol version (1.0.0).
instructions
object
Upload instructions including required headers and status check URL.
curl -X POST https://api.voyantcloud.com/v1/gallery/upload/direct \
  -H "Authorization: Bearer $VOYANT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fileName": "conference-recording.mp4",
    "expiryMinutes": 60,
    "metadata": {"event": "annual-conference-2025"}
  }'
{
  "uploadUrl": "https://upload.example.com/tus/abc123...",
  "fileId": "gal_file_xyz789",
  "expiresAt": "2025-01-15T12:30:00.000Z",
  "tusVersion": "1.0.0",
  "instructions": {
    "protocol": "TUS",
    "minChunkSize": 5242880,
    "headers": {
      "Tus-Resumable": "1.0.0"
    },
    "checkStatus": "/v1/gallery/files/gal_file_xyz789"
  }
}

Step 2: Upload the Video

Use any TUS client to upload directly to the uploadUrl. The upload is resumable—if interrupted, it can continue from where it left off.

JavaScript Example (tus-js-client)

import * as tus from "tus-js-client";

const file = document.getElementById("video-input").files[0];

const upload = new tus.Upload(file, {
  endpoint: uploadUrl, // From Step 1
  retryDelays: [0, 1000, 3000, 5000],
  chunkSize: 50 * 1024 * 1024, // 50MB chunks
  metadata: {
    filename: file.name,
    filetype: file.type,
  },
  onError: (error) => {
    console.error("Upload failed:", error);
  },
  onProgress: (bytesUploaded, bytesTotal) => {
    const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2);
    console.log(`${percentage}%`);
  },
  onSuccess: () => {
    console.log("Upload complete!");
  },
});

upload.start();

Python Example (tuspy)

from tusclient import client

tus_client = client.TusClient(upload_url)

uploader = tus_client.uploader(
    "./conference-recording.mp4",
    chunk_size=50 * 1024 * 1024,  # 50MB chunks
)

uploader.upload()
print("Upload complete!")

Step 3: Check Status

Poll the file endpoint or subscribe to webhooks to know when processing completes.
curl https://api.voyantcloud.com/v1/gallery/files/gal_file_xyz789 \
  -H "Authorization: Bearer $VOYANT_API_KEY"

Status Values

StatusDescription
pendingUpload URL created, waiting for upload
processingVideo is being processed
completedReady for playback
failedProcessing failed

Webhooks

Subscribe to these events for real-time notifications:
  • gallery.file.ready — Video processing completed successfully
  • gallery.file.failed — Video processing failed
For videos under 100MB, you can use the simpler Upload File endpoint with multipart form data.
Upload URLs expire after the specified expiryMinutes. If the URL expires before upload completes, you’ll need to create a new one.