---
title: "Request signed upload URL (recommended)"
method: POST
path: "/v2/workspaces/{workspace_id}/files/upload-url"
tags: ["Files"]
---

# Request signed upload URL (recommended)

`POST /v2/workspaces/{workspace_id}/files/upload-url`

Get a signed URL to upload a file directly to storage, bypassing the server. This is the recommended upload method for large files.

**Flow:**
1. Call this endpoint with content_type and file_size_bytes
2. POST to the returned upload_url with header `x-goog-resumable: start` to get a session URI
3. PUT your file content to the session URI (supports chunked/resumable uploads)
4. Use the returned file_id as input to generation endpoints

Allowed types: application/x-zip-compressed, application/zip, audio/mpeg, audio/wav, font/otf, font/ttf, image/jpeg, image/png, image/webp, video/mp4. Max size: 64 MB. Upload URL expires in 15 minutes.

## Path parameters

- `workspace_id` string, uuid, required — Id of the workspace that owns the resource.

## Headers

- `Idempotency-Key` string

## Request body

- RequestUploadUrlRequest
  - `content_type` string, required — MIME type of the file to upload.
  - `file_name` string, nullable — Optional file name.
  - `file_size_bytes` integer, required — Expected file size in bytes. Used to enforce upload limits.
  - `source_type` 'pose' | 'reference' | 'mask' | 'composite' | 'google_drive' | 'miro' | 'transformed_reference' | 'skill_resource' | 'skill_bundle'

## Response `201`

Successful Response

- RequestUploadUrlResponse
  - `file_id` string, uuid, required — Unique identifier for the file. Use as input to generation endpoints after upload completes.
  - `upload_url` string, required — Signed upload URL. POST to this URL to initiate a resumable upload, then PUT file chunks to the session URI returned in the Location header. Expires in 15 minutes.
  - `content_type` string, required — MIME type to use when uploading.
  - `source_type` 'pose' | 'reference' | 'mask' | 'composite' | 'google_drive' | 'miro' | 'transformed_reference' | 'skill_resource' | 'skill_bundle'

## Other responses

- `401` — Unauthenticated — missing or invalid Bearer token.
- `403` — Forbidden — insufficient permissions, or the access token lacks the scope the operation requires.
- `404` — Resource not found.
- `409` — The request with this `Idempotency-Key` is still running — retry after the number of seconds in `Retry-After`.
- `422` — Invalid input parameters, or an `Idempotency-Key` reused for a different request.
- `429` — Rate limited — retry after the number of seconds in `Retry-After`.
- `500` — Internal server error.

---

[API](https://skmtc.dev/layer/apis/layer-rest-api.md) · [All operations](https://skmtc.dev/layer/apis/layer-rest-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/layer/layer-rest-api/revisions/fafc779f9ba2/schema)
