---
title: "Declare a file and get upload instructions"
method: POST
path: "/media"
tags: ["Media"]
---

# Declare a file and get upload instructions

`POST /media`

## Request

- Base URL: `https://preflight.social/v1`
- URL: `https://preflight.social/v1/media`
- Auth: HTTP bearer

Either sizeBytes for a direct upload (signed URLs come back: one PUT, or one per part for files over 16 MB) or sourceUrl for the server to fetch. Scope media:write. Up to 60 a minute.

## Request body

- MediaInput
  - `kind` 'image' | 'video'
  - `mimeType` string
  - `sizeBytes` integer: Exact size; required for a direct upload.
  - `fileName` string
  - `durationSeconds` number: Measured by the client; lets a dry run warn about length limits.
  - `fps` number
  - `width` integer
  - `height` integer
  - `sourceUrl` string, uri: A public https URL the server fetches instead of an upload.

## Response `201`

The record and where to put the bytes.

- UploadInstructions: Where to PUT the bytes. The file never passes through the API.
  - `media` Media
    - `id` string, uuid
    - `kind` 'image' | 'video'
    - `status` 'awaiting_upload' | 'ingesting' | 'processing' | 'ready' | 'failed' | 'deleted'
    - `mimeType` string, nullable
    - `sizeBytes` integer, nullable
    - `durationSeconds` number, nullable
    - `width` integer, nullable
    - `height` integer, nullable
    - `fps` number, nullable
    - `fileName` string, nullable
    - `previewUrl` string, uri, nullable: A short-lived link to the file.
    - `error` string, nullable
    - `createdAt` string, date-time
  - `upload` object
    - `mode` 'single' | 'multipart' | 'ingest'
    - `uploadUrl` string, uri: single: PUT the whole file here, then POST /media/{id}/uploaded.
    - `headers` object
    - `partSize` integer: multipart: bytes per part; the last is shorter.
    - `parts` object[]: multipart: PUT each part, keep its ETag, then POST /media/{id}/complete.
      - `partNumber` integer
      - `url` string, uri

## Other responses

- `400`: The body did not validate (invalid_request; field says which).
- `401`: Missing, expired or unrecognised key.
- `402`: The plan's allowance: post_limit, upload_limit, file_too_large, channel_limit, api_not_on_plan, ai_limit, ai_not_in_plan. Carries limit and upgradeTo.
- `413`: file_too_large: over the server's ceiling.
- `429`: Rate limited; retry after the Retry-After header.

---

[API](https://skmtc.dev/preflight/apis/preflight-api.md) · [All operations](https://skmtc.dev/preflight/apis/preflight-api/llms.txt) · [OpenAPI document](https://skmtc.dev/preflight/apis/preflight-api/revisions/45ae7944ce71?raw)
