Skip to content

Request signed upload URL (recommended)

Viewing v1— superseded by v2

POST
/v1/workspaces/{workspace_id}/files/upload-url
curl --request POST \
--url https://api.app.layer.ai/api/v1/workspaces/:workspace_id/files/upload-url \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"content_type": "<string>",
"file_name": "<string>",
"file_size_bytes": 0,
"source_type": "pose"
}'

v1 is superseded. v1 was superseded by v2 on 2026-08-26. A sunset date has not been announced yet; v1 will be deprecated as a whole before removal. Migrate with the v2 migration guide.

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/heic, image/heic-sequence, image/heif, image/heif-sequence, image/jpeg, image/png, image/webp, model/gltf-binary, video/mp4. Max size: 100 MB for video, meshes and zip bundles, 64 MB for images and audio, 16 MB for fonts. Upload URL expires in 15 minutes.

workspace_id
required
Workspace Id

Id of the workspace that owns the resource.

string format: uuid

Id of the workspace that owns the resource.

Idempotency-Key
string
<= 255 characters

Opaque key making a retry of this request return the first result. A 2xx response is replayed for 24 hours with Idempotent-Replayed: true. A 4xx releases the key. A 5xx, or a request still running, holds the key for 120 seconds, during which a retry gets 409; after that a retry executes again. The key covers the method, path, query, and byte-identical body it was first used with; reusing it for a different request is rejected with 422. Protection is best effort: if the key store is unavailable, or a record is lost early, a retry can execute again even after an earlier 409. See the Idempotency section of the API description.

Media typeapplication/json
RequestUploadUrlRequest
object
content_type
required
Content Type

MIME type of the file to upload.

string
file_name
File Name

Optional file name.

string
nullable
file_size_bytes
required
File Size Bytes

Expected file size in bytes. Used to enforce upload limits.

integer
> 0
source_type
Any of:
FileSourceType
string
Allowed values: pose reference mask composite google_drive miro transformed_reference skill_resource skill_bundle

Successful Response

Media typeapplication/json
RequestUploadUrlResponse
object
file_id
required
File Id

Unique identifier for the file. Use as input to generation endpoints after upload completes.

string format: uuid
upload_url
required
Upload Url

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.

string
content_type
required
Content Type

MIME type to use when uploading.

string
source_type
Any of:
FileSourceType
string
Allowed values: pose reference mask composite google_drive miro transformed_reference skill_resource skill_bundle
Example
{
"source_type": "pose"
}

Unauthenticated — missing or invalid Bearer token.

Media typeapplication/problem+json
object
type
required
string
title
required
string
status
required
integer
detail
required
string
Example
{
"type": "https://api.layer.ai/errors/ERROR_CODE",
"title": "Error Title",
"status": 400,
"detail": "Human-readable description."
}

Forbidden — insufficient permissions, or the access token lacks the scope the operation requires.

Media typeapplication/problem+json
object
type
required
string
title
required
string
status
required
integer
detail
required
string
Example
{
"type": "https://api.layer.ai/errors/ERROR_CODE",
"title": "Error Title",
"status": 400,
"detail": "Human-readable description."
}

Resource not found.

Media typeapplication/problem+json
object
type
required
string
title
required
string
status
required
integer
detail
required
string
Example
{
"type": "https://api.layer.ai/errors/ERROR_CODE",
"title": "Error Title",
"status": 400,
"detail": "Human-readable description."
}

The outcome of the request with this Idempotency-Key is not known yet: it is still running, or it failed without a stored response. Retry after the number of seconds in Retry-After.

Media typeapplication/problem+json
object
type
required
string
title
required
string
status
required
integer
detail
required
string
Example
{
"type": "https://api.layer.ai/errors/ERROR_CODE",
"title": "Error Title",
"status": 400,
"detail": "Human-readable description."
}

Invalid input parameters, or an Idempotency-Key reused for a different request.

Media typeapplication/problem+json
object
type
required
string
title
required
string
status
required
integer
detail
required
string
Example
{
"type": "https://api.layer.ai/errors/ERROR_CODE",
"title": "Error Title",
"status": 400,
"detail": "Human-readable description."
}

Rate limited — retry after the number of seconds in Retry-After.

Media typeapplication/problem+json
object
type
required
string
title
required
string
status
required
integer
detail
required
string
Example
{
"type": "https://api.layer.ai/errors/ERROR_CODE",
"title": "Error Title",
"status": 400,
"detail": "Human-readable description."
}

Internal server error.

Media typeapplication/problem+json
object
type
required
string
title
required
string
status
required
integer
detail
required
string
Example
{
"type": "https://api.layer.ai/errors/ERROR_CODE",
"title": "Error Title",
"status": 400,
"detail": "Human-readable description."
}