Large file uploads

Files over the 100 MB direct-upload cap go through the resumable upload endpoint at /files/ — the same endpoint the app's uploader uses. It speaks the open tus 1.0 protocol, so any tus client works, and an interrupted transfer resumes where it left off instead of starting over. The maximum file size is 2 GB.

Requires the upload scope. Send the Authorization: Bearer header on every tus request. Note that one tus upload counts as two requests against the rate limit (creation and completion each verify the key).

The flow

  1. Create the upload: POST /files/ with the total size in Upload-Length and file metadata in Upload-Metadata. The response's Location header is the upload URL.
  2. Send bytes: PATCH the upload URL with Content-Type: application/offset+octet-stream and the current Upload-Offset.
  3. Resume after an interruption: HEAD the upload URL to read the server's Upload-Offset, then continue the PATCH from there.

The upload is admitted against your storage quota at creation (a full quota fails the create with 507), and the file is committed to the library when the last byte arrives.

Metadata

Upload-Metadata is a comma-separated list of key base64(value) pairs:

KeyRequiredDescription
nameyesFilename, used for type detection and display.
typenoMime type; inferred from name when omitted.
batchIdyesAny short id (up to 32 chars) grouping this upload — e.g. one id per import run.
collectionIdnoAdd the file to a collection you have access to.

With curl

# 1. create — note the Location header in the response
curl -i -X POST https://your-host/files/ \
  -H "Authorization: Bearer na_your_key" \
  -H "Tus-Resumable: 1.0.0" \
  -H "Upload-Length: 734003200" \
  -H "Upload-Metadata: name $(echo -n 'movie.mp4' | base64), type $(echo -n 'video/mp4' | base64), batchId $(echo -n 'import-2026-07' | base64)"

# 2. send the bytes to the returned Location
curl -X PATCH https://your-host/files/24e533e02ec3bc40c387f1a0e460e216 \
  -H "Authorization: Bearer na_your_key" \
  -H "Tus-Resumable: 1.0.0" \
  -H "Upload-Offset: 0" \
  -H "Content-Type: application/offset+octet-stream" \
  --data-binary @movie.mp4

With tus-js-client

import { createReadStream, statSync } from "node:fs"
import { Upload } from "tus-js-client"

const path = "movie.mp4"

const upload = new Upload(createReadStream(path), {
  endpoint: "https://your-host/files/",
  uploadSize: statSync(path).size,
  chunkSize: 50 * 1024 * 1024,
  metadata: {
    name: "movie.mp4",
    type: "video/mp4",
    batchId: "import-2026-07",
  },
  headers: { Authorization: `Bearer ${process.env.API_KEY}` },
  onError: (error) => console.error("upload failed", error),
  onSuccess: () => console.log("upload finished"),
})

upload.start()

Getting the file id

The tus protocol has no response body on completion, so the file's library id is not returned by this flow. When your integration needs the id right away, either upload via POST /api/v1/files (returns the id immediately, files up to 100 MB) or find the file afterwards with search — for example by filtering on the upload's import_date and matching original_name in the results.