---
title: "Upload an image, video or PDF"
method: POST
path: "/v1/upload"
tags: ["Upload"]
---

# Upload an image, video or PDF

`POST /v1/upload`

## Request

- Base URL: `https://api.masko.ai`
- URL: `https://api.masko.ai/v1/upload`
- Auth: HTTP bearer

Accepts an image URL as JSON or an image/video file as multipart form data. Stores an asset owned by the caller and returns `data.asset_id` for use as a reference or `source_asset_id`. Malformed multipart data or a missing file returns 400. For a PDF such as a brand book, send JSON `{ "content_type": "application/pdf", "size": BYTES }` instead: the response adds a signed `upload_url`, valid for 15 minutes, where you PUT the file with `upload_headers`. The file goes straight to storage, so it is not limited by the request body size; storage refuses it above 50 MB. Then pass `asset_id` to POST /v1/mascots/from-brand-book. No credit cost.

Select Masko-API-Version: 2026-09-26 (or api_version=2026-09-26) for the canonical mascot contract shown first. Existing unversioned clients keep legacy collection fields. A mascot_id query filter also selects the canonical contract. Mixing identifier names returns 400.

## Query parameters

- `api_version` '2026-09-26' | 'legacy'

## Headers

- `Idempotency-Key` string
- `Masko-API-Version` '2026-09-26' | 'legacy'

## Request body

- union
  - UploadUrlBody
    - `url` string, uri, required: Public HTTP(S) image URL without credentials. Downloaded server-side with a 15-second deadline, public-address checks on every redirect, and a streamed 10MB limit. Private addresses are rejected. Images only (image/png, image/jpeg, image/webp, image/svg+xml, image/gif). SVGs are auto-converted to PNG. For video uploads or binary files, POST the same endpoint as multipart/form-data with a "file" field (max 10MB for images, 50MB for videos).
  - UploadSignedBody
    - `content_type` 'application/pdf', required: Request a signed upload URL instead of sending the file. Only PDFs, such as brand books; images and videos are sent in the request body.
    - `size` integer, required: File size in bytes, at most 50 MB.
    - `file_name` string: Original file name, kept with the upload.

## Response `200`

Uploaded asset

- UploadResponse
  - `data` UploadData, required
    - `asset_id` string, uuid, required
    - `upload_url` string, uri: Signed upload only: PUT the file here with upload_headers before expires_at.
    - `upload_method` 'PUT'
    - `upload_headers` object: Signed upload only: send exactly these headers with the PUT.
    - `expires_at` string

## Other responses

- `400`: Validation error
- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not found
- `409`: Conflict

---

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