---
title: "Generate presigned URL for model upload"
method: POST
path: "/v1/models/uploads"
tags: ["Models"]
---

# Generate presigned URL for model upload

`POST /v1/models/uploads`

Generate a presigned URL for uploading a custom model archive.

This is step 1 of the presigned URL workflow:
1. POST /models/uploads → Returns presigned_url + upload_id
2. PUT presigned_url with model archive (client uploads directly to S3)
3. POST /models/uploads/{upload_id}/confirm → Validates and creates model

The model will be stored at the organization level and can be enabled
in any namespace within the organization.

**Availability**: custom model upload is an **enterprise-tier** feature —
requests from other account tiers receive a 403. Contact support or
upgrade your plan to enable it (BACKE-2759: this gate was previously
undocumented and only discoverable by hitting the 403).

## Request body

- CreateModelUploadRequest — Request to generate a presigned URL for model archive upload. This is step 1 of the presigned URL workflow: 1. POST /models/uploads → Returns presigned_url + upload_id 2. PUT presigned_url with model archive (client uploads directly to S3) 3. POST /models/uploads/{upload_id}/confirm → Validates and creates model Requirements: - name: Model name (e.g., 'my_custom_embedder') - version: Semantic version (e.g., '1.0.0') - model_format: Format of the model weights - file_size_bytes: Expected archive size for quota validation
  - `name` string, required — Model name: 3-64 chars, letters/numbers/underscores, starts with a letter (no hyphens or spaces)
  - `version` string, required — Semantic version string
  - `model_format` 'safetensors' | 'onnx' | 'pytorch' | 'huggingface', required — Format of the model weights
  - `description` string, nullable — Optional description of the model
  - `file_size_bytes` integer, nullable — Expected file size in bytes for quota validation
  - `presigned_url_expiration` integer — Presigned URL expiration time in seconds (1-24 hours)
  - `resource_requirements` ModelResourceRequirements — Resource requirements for model deployment.
    - `num_cpus` number — Number of CPUs
    - `num_gpus` integer — Number of GPUs
    - `memory` integer — Memory in bytes (default 4GB)
  - `framework` string, nullable — ML framework (e.g., sentence-transformers, transformers)
  - `task_type` string, nullable — Task type (e.g., embedding, classification, generation)
  - `source` 'customer' | 'mixpeek' | 'system' — Who is uploading: 'customer' (default) or 'mixpeek' (professional services). Mixpeek-sourced models are non-exportable by default.

## Response `200`

Successful Response

- ModelPresignedURLResponse — Response containing presigned URL for model upload. After receiving this response: 1. PUT the model archive to `presigned_url` with Content-Type: application/gzip 2. Call POST /models/uploads/{upload_id}/confirm to finalize
  - `upload_id` string, required — Upload ID to use when confirming
  - `presigned_url` string, required — S3 presigned URL for PUT upload
  - `s3_key` string, required — S3 object key where file will be stored
  - `expires_at` string, date-time, required — When the presigned URL expires
  - `organization_id` string, required — Organization ID
  - `name` string, required — Model name
  - `version` string, required — Model version
  - `model_format` 'safetensors' | 'onnx' | 'pytorch' | 'huggingface', required — Model format

## Other responses

- `400` — Bad Request
- `401` — Unauthorized
- `403` — Forbidden
- `404` — Not Found
- `422` — Validation Error
- `500` — Internal Server Error

---

[API](https://skmtc.net/mixpeek/apis/mixpeek-api.md) · [All operations](https://skmtc.net/mixpeek/apis/mixpeek-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/mixpeek/mixpeek-api/versions/220a3b263fda/schema)
