Sovran
API documentation

Upload files

Reserve a signed upload, transfer a file, and wait for media preparation.

Uploads have three steps: reserve a Storage path, transfer the file, and complete the reservation. Then wait for media preparation. A completed transfer does not mean a video is ready to render.

Run a complete video upload

Use Node.js 22 or later and a key with Read and Write permissions. The workspace needs a current paid plan. Create a project first, or use a project ID from GET /api/v1/projects.

Save upload-video.mjs in a local directory. It is a standalone script; it defines the reserve request, TUS transfer, completion request, and preparation polling.

Upload one video to an existing project
pnpm add tus-js-client@4.3.1
export SOVRAN_API_ORIGIN="https://sovran.ai"
# Set SOVRAN_API_KEY through your secret store or server environment.
node upload-video.mjs PROJECT_ID ./hook.mp4 ./upload-state.json

Replace PROJECT_ID with a returned project ID. Use http://localhost:3000 for the local host. Use a key created on that host. The script accepts MP4, MOV, and WebM videos up to 1 GiB (1,073,741,824 bytes). The API also supports other declared media types; see the endpoint reference.

The script prints assetId and preparation when the asset is ready. It streams file chunks; it does not read the whole file into memory. The TUS transfer stops after 10 minutes. Preparation polling stops after 10 minutes.

Keep the state file if a transfer or request stops. Run the same command again with the same key and file to resume or read the saved result. Request tokens and the upload URL are saved before reuse. Use one process per state file. The file contains limited upload tokens; keep it private and out of version control. It does not contain the API key.

Reserve the upload

POST /api/v1/projects/{projectId}/uploads needs Write permission and Idempotency-Key. For a 10 MiB MP4 file, send:

POST /api/v1/projects/{projectId}/uploads — request body
{ "type": "video", "filename": "hook.mp4", "mimeType": "video/mp4", "sizeBytes": 10485760 }

Use the file's actual byte size. The extension and MIME type must match. Video files can be at most 1 GiB (1,073,741,824 bytes). Music and image files can be at most 500 MiB (524,288,000 bytes).

The HTTP 201 response wraps the reservation in data. Save its id as uploadId and its assetId for preparation checks. It also includes expiresAt, status, and upload:

Returned fieldUse
upload.urlThe signed TUS endpoint.
upload.headersThe exact upload headers, including x-signature.
upload.metadataThe exact Storage bucket, object path, and content type.
upload.chunkSizeBytesThe required chunk size: 6 MiB (6,291,456 bytes).
expiresAtThe reservation expiry time, two hours after creation.

Transfer with TUS

TUS is an upload protocol that can resume an interrupted transfer. The transfer goes directly to Supabase Storage. Use the returned URL, headers, metadata, and chunk size. The signed token authorizes only the reserved path. This transfer uses the upload token; it does not use the Sovran API key or a browser session.

The downloaded script uses tus-js-client. It saves the created upload URL and passes it back as uploadUrl on a later run. It uses bounded retries and 6 MiB chunks. Keep signed tokens out of logs. Voice clone upload reservations use the same signed TUS endpoint.

Complete the reservation

After the transfer succeeds, send an empty JSON object with a separate request token:

POST /api/v1/uploads/{uploadId}/complete — request body
{}

The response includes data.upload and data.asset. Sovran checks the reserved path, actual stored size, and media type. It also checks the first 4 KiB (4,096 bytes) of the file for a supported container header. A failed check returns an error. Do not supply preparation results or Storage paths in this request.

Use the same key that reserved the upload. Read GET /api/v1/uploads/{uploadId} for reservation status. If the reservation expired, check its status before reserving another upload. A completion replay with the same body and request token returns the saved result.

Pending uploads share a workspace limit of 10 reservations and 5 GiB of reserved capacity. Each reservation uses the bucket's maximum file size against that capacity. Complete pending uploads before reserving more. Expired reservations retain their capacity until cleanup succeeds. Read request and upload limits for cleanup and error details.

Wait for preparation

Read GET /api/v1/assets/{assetId}. This example shows the preparation fields from a ready video response:

GET /api/v1/assets/{assetId} — selected response fields
{ "preparation": { "state": "ready", "renderReady": true } }

Wait while preparation.state is processing. Stop if it is failed. Start a render only when preparation.renderReady is true. The standalone script starts with a 2-second polling delay and increases it to at most 15 seconds. On timeout, a repeated run reads the same asset.

API video preparation does not start automatic transcription. Use the creation tools when you need transcripts or captions. Continue with Create your first video to turn prepared clips into a finished video.

If a media header check fails

Use a supported extension and matching MIME type. The file must start with a supported container header.

  • MP3, AAC, or FLAC files with an ID3 metadata tag need their audio header within the first 4 KiB (4,096 bytes). Remove a larger tag or export the media again.
  • MP4, M4V, and M4A files need a file type box with a recognized media brand.
  • MOV and QT files need a QuickTime brand or a legacy moov, mdat, wide, or free header.
  • WebM files need a complete WebM header within the checked prefix.
  • Raw M4V streams and unknown ISO brands are not accepted.

The header check confirms the container format. Preparation also checks the media streams and codecs. See Limits for the full upload limits.

Was this article helpful?

On this page