Source: https://docs.sogni.ai/api-reference/media/

# Media and Image URLs

Presigned upload and download URLs for reference media and generated results. Uploads use a presigned **POST** with form fields and a server-enforced maximum file size. Media endpoints cover audio and video; the image endpoints add image-aware validation. Unlimited subscribers can also keep [saved uploads](https://docs.sogni.ai/api-reference/media/#saved-uploads) to reuse across projects.

### GET /v2/media/uploadUrl

Returns a presigned POST URL plus form fields for uploading a single media object up to `maxSizeBytes`. The form expires after one hour. Unknown query parameters return `400`. Uploading an input for a `jobId` already claimed by another account returns `403`.

### [#](https://docs.sogni.ai/api-reference/media/#query-parameters)Query parameters

| Name | Type | In | Description |
| --- | --- | --- | --- |
| typerequired | string | query | Asset type. Artist-side: `referenceAudio`, `referenceVideo`. Worker-side: `complete`, `preview`. |
| jobIdrequired | string | query | Job identifier the asset belongs to. |
| id | string | query | Artifact identifier. Required for worker uploads. For artist uploads, omit it, or use `referenceVideo1`–`referenceVideo3` or `referenceAudio1`–`referenceAudio3` to fill numbered reference slots; pass the same `id` when downloading. |
| contentType | string | query | Optional MIME type. Pinned so downloads sign the same S3 object key. |

### [#](https://docs.sogni.ai/api-reference/media/#response)Response

```json
{
  "status": "success",
  "data": {
    "url": "https://<bucket>.s3-accelerate.amazonaws.com/",
    "fields": {
      "Content-Type": "video/mp4",
      "bucket": "<bucket>",
      "key": "video/2026-05-18/<jobId>/reference.mp4",
      "X-Amz-Algorithm": "AWS4-HMAC-SHA256",
      "X-Amz-Credential": "…",
      "X-Amz-Date": "…",
      "Policy": "<base64-encoded-policy>",
      "X-Amz-Signature": "<hex-signature>"
    },
    "maxSizeBytes": 104857600,
    "allowedContentTypes": [ "…" ]
  }
}
```

`allowedContentTypes` depends on the asset type:

-   **Video uploads** such as `referenceVideo`: `video/mp4`, `video/quicktime`, `video/webm`
-   **Audio uploads** such as `referenceAudio`: `audio/mp4`, `audio/mpeg`, `audio/flac`, `audio/wav`, `audio/x-wav`, `audio/wave`
-   **Preview frames**: `image/jpeg`, `image/jpg`, `image/png`, `image/webp`

**How to upload.** POST to the returned `url` (don't hard-code the host) as `multipart/form-data`, including every key and value from `fields`, then your `file` field last. The storage service returns `204` on success.

### GET /v2/media/downloadUrl

Returns a presigned download URL, valid for two days, for a previously uploaded asset or a completed job artifact. A deleted project's downloads return `404`.

### [#](https://docs.sogni.ai/api-reference/media/#query-parameters-1)Query parameters

| Name | Type | In | Description |
| --- | --- | --- | --- |
| typerequired | string | query | Asset type. Same values as upload. |
| jobIdrequired | string | query | Job identifier. |
| id | string | query | Artifact id. |
| contentType | string | query | Optional MIME hint. The server uses the stored upload content type when available. |
| artifact | string | query | Optional. `lastFrame` returns the exported final frame of a Seedance 2.5 video that requested one; otherwise `404`. |

### [#](https://docs.sogni.ai/api-reference/media/#response-1)Response

```json
{
  "status": "success",
  "data": { "downloadUrl": "https://<bucket>.s3-accelerate.amazonaws.com/…?X-Amz-…" }
}
```

### GET /v2/image/uploadUrl

Returns a presigned POST URL plus form fields for image uploads (PNG, JPEG, WEBP, GIF). Same flow as media, with image-aware validation and content-type pinning.

### [#](https://docs.sogni.ai/api-reference/media/#query-parameters-2)Query parameters

| Name | Type | In | Description |
| --- | --- | --- | --- |
| typerequired | string | query | Asset type. Artist-side: `startingImage`, `cnImage`, `contextImage1`–`contextImage16`, `referenceImage`, `referenceImageEnd`, `referenceMask`. Worker-side: `complete`, `preview`. |
| jobIdrequired | string | query | Job identifier. |
| imageId | string | query | Artifact id. Required for worker uploads. |
| contentType | string | query | Optional MIME type, pinned for download. |

### [#](https://docs.sogni.ai/api-reference/media/#response-2)Response

```json
{
  "status": "success",
  "data": {
    "url": "https://<bucket>.s3-accelerate.amazonaws.com/",
    "fields": {
      "Content-Type": "image/png",
      "bucket": "<bucket>",
      "key": "2026-05-18/<jobId>/startingImage.png",
      "X-Amz-Algorithm": "AWS4-HMAC-SHA256",
      "X-Amz-Credential": "…",
      "X-Amz-Date": "…",
      "Policy": "<base64-encoded-policy>",
      "X-Amz-Signature": "<hex-signature>"
    },
    "maxSizeBytes": 104857600,
    "allowedContentTypes": ["image/png", "image/jpeg", "image/jpg", "image/webp", "image/gif"]
  }
}
```

### GET /v2/image/downloadUrl

Returns a presigned download URL for an image artifact. Query parameters match the image upload endpoint.

### [#](https://docs.sogni.ai/api-reference/media/#response-3)Response

```json
{
  "status": "success",
  "data": {
    "downloadUrl": "https://<bucket>.s3-accelerate.amazonaws.com/…?X-Amz-…"
  }
}
```

**Saved uploads.** Unlimited subscribers can save an image, video, or audio file once and reuse it across projects without uploading it again. Saved uploads are private to the account. The [JavaScript and Python SDKs](https://docs.sogni.ai/sogni-sdk/) use them automatically when you pass files to `projects.create()`, and expose `projects.assets` for explicit control; durable workflows take `media_references` URLs instead. Prepare, finalize, and bind require an active Unlimited plan (`403` otherwise); listing, capabilities, and removal do not. Files are kept while the plan is active, and for 30 days after the later of the paid period's end and the last save or reuse.

### GET /v1/assets

Returns `data.assets`, newest first, and `data.limits` (`fileBytes`, `idleDays`). Each record has `id`, `name`, `bytes`, `contentType`, `state` (`uploading` or `ready`), `createdAt`, and `expiresAt` (Unix milliseconds).

### GET /v1/assets/capabilities

Returns `{ enabled, limits }`. `enabled` is `false` without an active plan or when saved storage is unavailable.

### POST /v1/assets/prepare

### [#](https://docs.sogni.ai/api-reference/media/#body)Body

| Name | Type | In | Description |
| --- | --- | --- | --- |
| sha256required | string | body | Lowercase hex SHA-256 of the file. |
| bytesrequired | integer | body | File size, from 1 to `limits.fileBytes`. |
| contentTyperequired | string | body | `image/png`, `image/jpeg`, `image/webp`, `video/mp4`, `video/quicktime`, `video/webm`, `audio/mp4`, `audio/mpeg`, `audio/flac`, `audio/wav`, `audio/x-wav`, or `audio/wave`. |
| name | string | body | Optional label, up to 80 characters. |

If the same file is already saved, returns its record with `reused: true` and nothing to upload. Otherwise returns the record with `reused: false`, an `uploadUrl`, and `uploadHeaders`. `PUT` the exact bytes to `uploadUrl` within five minutes, sending exactly those headers. An upload that is never finalized expires after one hour.

### POST /v1/assets/:id/finalize

Send a JSON body (`{}` is fine). Verifies size, type, and checksum, then returns the `ready` record. A mismatch returns `409`, an expired upload `410`, and a busy entry `423`.

### POST /v1/assets/:id/bind

Copies a ready saved upload into a project input slot before you submit the project through the SDK. Body: `{ projectId, type, id? }`. `projectId` is the UUID you will submit; `type` is an image input type from the [image upload endpoint](https://docs.sogni.ai/api-reference/media/#images), `referenceVideo`, or `referenceAudio`, and must match the file's media type; `id` selects a numbered reference slot. Returns `{ assetId, projectId, type, contentType }`. A project ID already claimed by another account returns `403`.

### DELETE /v1/assets/:id

Deletes the saved file; `data` is `{}`. Inputs already copied into existing projects are not affected. An unknown ID, or one owned by another account, returns `404`.
