> ## Documentation Index
> Fetch the complete documentation index at: https://opentrain.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Prepare Asset Upload

> Start a private media upload: declare metadata, receive a signed PUT target.

Starts the two-phase private upload: declare the file's metadata and hash, receive a short-lived signed `PUT` target. Upload the bytes there, then call [finalize](/docs/developers/api-reference/instructions/finalize-asset). Size limits per kind: **IMAGE 25 MB, FILE 100 MB, AUDIO 250 MB, VIDEO 2 GB** (confirm live values via [capabilities](/docs/developers/api-reference/instructions/capabilities)). Canonical documents reference assets **by id only** — raw storage URLs are rejected at write time.

**Requirements:** `instructions:write` + workspace `EDITOR` or `OWNER` + a verified (claimed) account.

## Request

<ParamField body="workspaceId" type="string" required>
  The owning workspace (assets never cross workspace boundaries).
</ParamField>

<ParamField body="filename" type="string" required>
  Original filename.
</ParamField>

<ParamField body="mimeType" type="string" required>
  MIME type — also determines the default `kind`.
</ParamField>

<ParamField body="sizeBytes" type="number" required>
  Exact byte size, checked against the per-kind limit.
</ParamField>

<ParamField body="sha256" type="string" required>
  Lowercase hex SHA-256 of the file bytes.
</ParamField>

<ParamField body="kind" type="string">
  Override: `IMAGE`, `FILE`, `AUDIO`, or `VIDEO`.
</ParamField>

## Response

Returns `201`.

<ResponseField name="asset" type="object">
  The `PENDING` asset record.
</ResponseField>

<ResponseField name="upload" type="object">
  `{url, method: "PUT", headers, expiresAt}` — send the raw bytes to `url` with the given headers before `expiresAt`.
</ResponseField>
