Skip to content

Uploads

Create direct or multipart upload sessions, send storage bytes, and complete processing.

This guide walks you through creating an upload session, sending video bytes, and monitoring processing until the video is ready.

The upload flow uses two permissions: UPLOAD_VIDEOS for upload-session operations and READ_VIDEOS for the video resource that reports processing status. Video bytes go directly to the short-lived storage target returned by the API.

Create A Session

curl --fail-with-body -X POST "$BASE_URL/api/v1alpha1/videos" \
  -H "Authorization: Bearer $DEEPFRAME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: upload-create-01" \
  -d '{
    "filename": "warehouse-inspection.mp4",
    "file_size": 157286400
  }'

The request contains only the original filename and exact file_size that the caller knows before upload. The server owns content type and measures duration during processing. Upload strategy is always automatic: files up to 200 MiB use one direct PUT, and larger files use multipart upload. Do not send a strategy preference.

The response contains the public video_id, the selected strategy, session state, expiry, byte counters, and either target or targets. A storage target has method: "put", a lowercase enum token that means use HTTP PUT for the storage request.

Direct Upload

Use the returned target exactly before expires_at:

curl --fail-with-body -X PUT "$TARGET_URL" \
  -H "Content-Type: video/mp4" \
  --data-binary @warehouse-inspection.mp4

Send every key and value in the target's headers map exactly as returned. Header-map keys are verbatim, including their case; do not rename them or convert them to snake_case. Do not send the DeepFrame API key to storage.

Multipart Upload

The session returns targets and a chunk_size. Upload each part to its target. If a target expires, request fresh targets:

curl --fail-with-body -X POST "$BASE_URL/api/v1alpha1/videos/$VIDEO_ID/upload/targets" \
  -H "Authorization: Bearer $DEEPFRAME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: upload-targets-01" \
  -d '{"part_numbers":[1,2,3]}'

Record each storage ETag exactly, including quotes when present:

curl --fail-with-body -X PUT "$BASE_URL/api/v1alpha1/videos/$VIDEO_ID/upload/parts" \
  -H "Authorization: Bearer $DEEPFRAME_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"parts":[{"part_number":1,"etag":"\"etag-part-1\""},{"part_number":2,"etag":"\"etag-part-2\""}]}'

Repeating the same part receipt with the same ETag is safe. A different ETag for a recorded part is rejected.

Complete The Upload

The completion body is {}. The API verifies the stored object against file_size before processing it:

curl --fail-with-body -X POST "$BASE_URL/api/v1alpha1/videos/$VIDEO_ID/upload/complete" \
  -H "Authorization: Bearer $DEEPFRAME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: upload-complete-01" \
  -d '{}'

The 202 response means the video entered processing. It does not mean indexing is complete. Do not use the video as a source_ids value for an analysis run until its processing status is completed.

Monitor Processing

Use GET /api/v1alpha1/videos/{video_id} with READ_VIDEOS:

curl --fail-with-body "$BASE_URL/api/v1alpha1/videos/$VIDEO_ID" \
  -H "Authorization: Bearer $DEEPFRAME_API_KEY"

The video status values are queued, processing, completed, and failed. Only completed means that the video is searchable and ready for source_ids in an analysis run. Use progress for a best-effort percentage and the nested error object when a video fails. Upload session state is separate from video processing status. error_message is a deprecated field that repeats the curated error detail; see Errors.

GET /api/v1alpha1/videos/{video_id}/upload reports the upload session and uses UPLOAD_VIDEOS. DELETE /api/v1alpha1/videos/{video_id}/upload aborts an open session and returns 204.

Retry Rules

Use Idempotency-Key for session creation, target refresh, and completion. PUT /videos/{video_id}/upload/parts has no idempotency header; its receipt contract is idempotent when the same ETag is repeated. Never change the body while reusing an idempotency key.

On this page