---
title: "Create Video Upload"
method: POST
path: "/v1/video-jobs/uploads"
tags: ["video-jobs"]
---

# Create Video Upload

`POST /v1/video-jobs/uploads`

Mint a presigned PUT URL the browser can upload a video directly to.

Flow (PR B3): dashboard calls this → gets `{upload_url, s3_key}`
→ PUTs the file straight to S3 over XHR (with onprogress for the
upload progress bar) → then POSTs `/v1/video-jobs` with
`source_kind='upload'` + the same `s3_key`. Backend never streams
the video through itself — 2 GB through a Python uvicorn worker
would pin it for minutes.

The S3 key shape is owned by `video_upload_s3_prefix` in the
service module so this minter and the job-create validator both
agree on the namespace (review-fixup contract from PR #756).

Filename gets sanitized to prevent path-traversal / weird-char
surprises in S3 listings — the original filename is also stored
in the job row as part of the slug.

## Request body

- CreateUploadRequest
  - `filename` string, required — Original filename — used as the slug seed + retained as the S3 key suffix.
  - `content_type` string — MIME type the browser will send with the PUT.

## Response `200`

Successful Response

- object

## Other responses

- `422` — Validation Error

---

[API](https://skmtc.dev/roxels/apis/interviewer.md) · [All operations](https://skmtc.dev/roxels/apis/interviewer/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/roxels/interviewer/revisions/4ef48ed226e1/schema)
