Skip to main content

Upload with the REST API

Drive the resumable TUS upload protocol over plain HTTP — create the session, transfer the file in chunks, and resume after interruptions.

No SDK? You can drive the resumable upload flow yourself over plain HTTP. It’s the TUS protocol: create a session, then PATCH the bytes in chunks. This is exactly what the SDKs do under the hood — prefer them unless you can’t.

All calls use your API key (Authorization: Bearer sk_…) and the Write Assets scope, against the base URL https://api.videas.com.

Step 1 — create the upload session

POST /upload/ with the TUS headers. Upload-Metadata is a comma-separated list of key <base64(value)> pairs; filename and workspace_uid are required (optional: asset_name, description, parent_folder_uid, content_type).

# echo -n "lecture.mp4" | base64  → bGVjdHVyZS5tcDQ=
# echo -n "ws_123"      | base64  → d3NfMTIz
curl -i -X POST https://api.videas.com/api/external/v1/upload/ \
  -H "Authorization: Bearer $VIDEAS_API_KEY" \
  -H "Tus-Resumable: 1.0.0" \
  -H "Upload-Length: 734003200" \
  -H "Upload-Metadata: filename bGVjdHVyZS5tcDQ=,workspace_uid d3NfMTIz"

The 201 response carries a Location header (where to send the bytes) and a body with asset_uid (usable immediately) and expires_at:

{ "uid": "…", "location": "/api/external/v1/upload/<session>", "asset_uid": "ast_…", "total_size": 734003200, "expires_at": "…" }

If location is relative (as above), resolve it against the base URL: https://api.videas.com/api/external/v1/upload/<session>.

Step 2 — transfer the file in chunks

Send the bytes with one or more PATCH requests. Each request:

  • carries Upload-Offset (how many bytes already sent — starts at 0),
  • uses Content-Type: application/offset+octet-stream,
  • must not exceed 50 MiB of body.
# First chunk: bytes 0 .. 50 MiB
curl -i -X PATCH "https://api.videas.com/api/external/v1/upload/<session>" \
  -H "Authorization: Bearer $VIDEAS_API_KEY" \
  -H "Tus-Resumable: 1.0.0" \
  -H "Upload-Offset: 0" \
  -H "Content-Type: application/offset+octet-stream" \
  --data-binary @chunk-0.bin

Each 204 response returns the new Upload-Offset — use it as the offset of the next chunk, and repeat until you’ve sent Upload-Length bytes. When the last chunk lands, the response includes X-Checksum-Blake3.

A tiny loop, in pseudo-code:

offset = 0
while offset < total:
    end   = min(offset + CHUNK, total)          # CHUNK ≤ 50 MiB
    PATCH location, Upload-Offset: offset, body = bytes[offset:end]
    offset = response.headers["Upload-Offset"]  # server's authoritative offset

Resuming after an interruption

If a PATCH fails or the connection drops, ask the server how far it got with a HEAD, then continue from there:

curl -I "https://api.videas.com/api/external/v1/upload/<session>" \
  -H "Authorization: Bearer $VIDEAS_API_KEY" -H "Tus-Resumable: 1.0.0"
# → Upload-Offset: 314572800   (resume PATCH from this offset)

A 409 Conflict on a PATCH means your Upload-Offset didn’t match the server’s; the response’s Upload-Offset header tells you the correct one. Sessions expire after 24 hours — start over with a new session past that.

After the upload: wait for processing

The asset_uid exists immediately, but the video is processed asynchronously. Poll it until ready:

curl https://api.videas.com/api/external/v1/assets/ast_.../ \
  -H "Authorization: Bearer $VIDEAS_API_KEY"
# → { "status": "ready", … }  (don't play/embed until "ready")

Next steps