---
title: "Batch Create Uploads"
method: POST
path: "/v1/buckets/{bucket_identifier}/uploads/batch"
tags: ["Bucket Uploads"]
---

# Batch Create Uploads

`POST /v1/buckets/{bucket_identifier}/uploads/batch`

## Request

- Base URL: `https://api.mixpeek.com`
- URL: `https://api.mixpeek.com/v1/buckets/{bucket_identifier}/uploads/batch`
- Auth: HTTP bearer and API key in header `X-Namespace`

Generate multiple presigned URLs in a single request.

    All uploads belong to the same bucket (from path parameter).
    Maximum 100 uploads per batch.

    Shared metadata is merged with individual upload metadata (individual takes precedence).

## Path parameters

- `bucket_identifier` string, required: The unique identifier of the bucket

## Request body

- BatchUploadRequest: Request to generate multiple presigned URLs in a single request.
  - `uploads` CreateUploadRequest[], required: List of upload requests (max 100)
    - `filename` string, required: Name of the file to upload. REQUIRED. Must be a valid filename without path traversal characters (../, \). The filename is used to derive the blob_property if not explicitly provided. Examples: 'product_video.mp4', 'thumbnail.jpg', 'transcript.txt'
    - `content_type` string, required: MIME type of the file. REQUIRED. Must be a valid MIME type (e.g., 'video/mp4', 'image/jpeg', 'application/pdf'). The presigned URL will enforce this content type during upload. Used to validate compatibility with bucket schema if create_object_on_confirm=true.
    - `file_size_bytes` integer, nullable: Expected file size in bytes. OPTIONAL but RECOMMENDED. If provided, will be validated against: 1. Tier-based file size limits (100MB free, 5GB pro, 50GB enterprise) 2. Storage quota availability 3. Actual uploaded file size during confirmation. If not provided, quota checking is skipped until confirmation.
    - `presigned_url_expiration` integer: How long the presigned URL is valid, in seconds. OPTIONAL, defaults to 3600 (1 hour). Valid range: 60 seconds (1 minute) to 86400 seconds (24 hours). After expiration, the URL cannot be used and you must request a new one. Recommendation: Use shorter expiration (300-900 seconds) for security-sensitive files, longer expiration (3600-7200 seconds) for large files that take time to upload.
    - `metadata` object: Custom metadata for tracking purposes. OPTIONAL. Stored with the upload record for filtering and analytics. Does NOT affect the created bucket object (use object_metadata for that). Common uses: campaign tracking, user identification, upload source.
    - `create_object_on_confirm` boolean: Whether to automatically create a bucket object when upload is confirmed. OPTIONAL, defaults to TRUE (object is created automatically). If true (default): - Bucket MUST have a schema defined - blob_property must exist in bucket schema - content_type must match schema field type - Validation happens BEFORE generating presigned URL - Object is created automatically on confirmation. - The object is created in PENDING status and is not processed. Processing starts when it is added to a batch (POST /buckets/{id}/batches then POST /buckets/{id}/batches/{batch_id}/submit), or when the confirm call sets auto_submit_batch=true. If false: - Upload is confirmed but no object is created - Use this when combining multiple uploads into one object - Reference the upload_id later in POST /buckets/{id}/objects.
    - `object_metadata` object, nullable: Metadata to attach to the created bucket object. OPTIONAL. Only used if create_object_on_confirm=true. This metadata will be: 1. Validated against bucket schema (if keys match schema fields) 2. Attached to the bucket object 3. Passed to downstream documents in connected collections. Example: {'priority': 'high', 'category': 'products', 'tags': ['featured']}
    - `blob_property` string, nullable: Property name for the blob in the bucket object. OPTIONAL. When omitted: defaults to the bucket schema's file-type property, if it declares exactly one; otherwise falls back to the filename without its extension (e.g. 'product_video.mp4' -> 'product_video'), which fails if the stem is not alphanumeric_with_underscores (a hyphenated filename, for example) -- pass this field explicitly to avoid that. If create_object_on_confirm=true: - Must exist in bucket schema - Must be alphanumeric with underscores only - Will be validated BEFORE generating presigned URL. Common values: 'video', 'image', 'thumbnail', 'transcript', 'content'.
    - `blob_type` string, nullable: Type of blob. OPTIONAL. Defaults to type derived from content_type (e.g., 'video/mp4' → 'VIDEO'). Must be a valid BucketSchemaFieldType if provided. Valid values: IMAGE, VIDEO, AUDIO, TEXT, PDF, DOCUMENT, etc. If create_object_on_confirm=true, will be validated against bucket schema field type.
    - `file_hash` string, nullable: SHA256 hash of the file content for duplicate detection. OPTIONAL. If provided: - System checks for existing confirmed uploads with same hash - If duplicate found and skip_duplicates=true, returns existing upload - The hash is kept as the content address for this upload, and is what later uploads are matched against - It is taken on trust. A single-part ETag is an MD5 of the same bytes, so the object store cannot confirm a SHA256 against what it holds. Send a hash of the bytes you actually upload. If not provided: - Hash is calculated from S3 ETag after upload - Duplicate detection only happens during confirmation. Use case: Pre-calculate hash client-side to avoid uploading duplicates. Format: 64-character hexadecimal string (SHA256), lower case. An upper-case digest is rejected.
    - `skip_duplicates` boolean: Skip upload if a file with the same hash already exists. OPTIONAL, defaults to TRUE. If true (default): - If file_hash provided: System checks MongoDB for existing completed upload with same hash - If duplicate found: Returns existing upload details WITHOUT generating new presigned URL - If file_hash NOT provided: Duplicate check happens during confirmation using S3 ETag - Saves bandwidth, storage, and upload time by reusing existing files. If false: - Always generates new presigned URL even if file already uploaded - Creates separate upload record for same file content - Useful when you need distinct upload tracking for identical files. Recommendation: Keep default (true) unless you specifically need multiple upload records for same file.
  - `shared_metadata` object, nullable: Metadata to apply to all uploads (merged with individual metadata)
  - `shared_object_metadata` object, nullable: Object metadata to apply to all uploads (merged with individual)

## Response `200`

Successful Response

- BatchUploadResponse: Response from batch upload request.
  - `uploads` UploadResponse[], required: Generated uploads with presigned URLs
    - `upload_id` string, required: Unique identifier for this upload. Auto-generated. ⚠️ NEXT STEP: After uploading to S3, you MUST confirm: POST /v1/uploads/{upload_id}/confirm Other operations: - Check status: GET /v1/uploads/{upload_id} - Cancel upload: DELETE /v1/uploads/{upload_id} Format: 'upl_' followed by 16 random characters.
    - `bucket_id` string, required: Target bucket ID where object will be created
    - `filename` string, required: Name of the file to upload
    - `content_type` string, required: MIME type enforced by the presigned URL
    - `file_size_bytes` integer, nullable: Expected file size in bytes if provided in request. Will be validated during confirmation.
    - `presigned_url` string, uri, nullable: Time-limited HTTPS URL for uploading directly to S3. **Step 1 - Upload to S3:** curl -X PUT '{presigned_url}' -H 'Content-Type: {content_type}' --upload-file {filename} **Step 2 - REQUIRED: Confirm the upload:** POST /v1/uploads/{upload_id}/confirm (S3 has no callback - you MUST call confirm to finalize) The URL includes authentication and expires after presigned_url_expiration seconds. S3 returns an ETag header on success - pass it to confirm for integrity validation. NOTE: This will be null if is_duplicate=true (duplicate found, no upload needed).
    - `presigned_url_expiration` integer, required: How long the presigned URL is valid, in seconds
    - `s3_key` string, required: Full S3 object key where the file will be stored. Format: {internal_id}/{namespace_id}/api_buckets_uploads_create/{upload_id}/{filename}. Used internally for verification and object creation.
    - `status` 'PENDING' | 'QUEUED' | 'IN_PROGRESS' | 'PROCESSING' | 'COMPLETED' | 'COMPLETED_WITH_ERRORS' | 'FAILED' | 'CANCELED' | 'INTERRUPTED' | 'UNKNOWN' | 'SKIPPED' | 'DRAFT' | 'ACTIVE' | 'ARCHIVED' | 'SUSPENDED' | 'DEACTIVATED', required: Enumeration of task statuses for tracking asynchronous operations. Task statuses indicate the current state of asynchronous operations like batch processing, object ingestion, clustering, and taxonomy execution. Status Categories: Operation Statuses: Track progress of async operations Lifecycle Statuses: Track entity state (buckets, collections, namespaces) Values: PENDING: Task is queued but has not started processing yet IN_PROGRESS: Task is currently being executed PROCESSING: Task is actively processing data (similar to IN_PROGRESS) COMPLETED: Task finished successfully with no errors COMPLETED_WITH_ERRORS: Task finished but some items failed (partial success) FAILED: Task encountered an error and could not complete CANCELED: Task was manually canceled by a user or system UNKNOWN: Task status could not be determined SKIPPED: Task was intentionally skipped DRAFT: Task is in draft state and not yet submitted ACTIVE: Entity is active and operational (for buckets, collections, etc.) ARCHIVED: Entity has been archived SUSPENDED: Entity has been temporarily suspended Terminal Statuses: COMPLETED, COMPLETED_WITH_ERRORS, FAILED, CANCELED are terminal statuses. Once a task reaches these states, it will not transition to another state. Partial Success Handling: COMPLETED_WITH_ERRORS indicates that the operation completed but some documents/items failed. The task result includes: - List of successful items - List of failed items with error details - Success rate percentage This allows clients to handle partial success scenarios appropriately. Polling Guidance: - Poll tasks in PENDING, QUEUED, IN_PROGRESS, or PROCESSING states - Stop polling when task reaches COMPLETED, COMPLETED_WITH_ERRORS, FAILED, or CANCELED - Use exponential backoff (1s → 30s) when polling
    - `metadata` object: Custom metadata for tracking
    - `create_object_on_confirm` boolean, required: Whether bucket object will be auto-created on confirmation
    - `object_metadata` object, nullable: Metadata for the bucket object (if create_object_on_confirm=true)
    - `blob_property` string, nullable: Property name for the blob in bucket object
    - `blob_type` string, nullable: Type of blob (IMAGE, VIDEO, etc.)
    - `file_hash` string, nullable: SHA256 hash of the file content. Set during confirmation from S3 metadata or provided in request. Used for duplicate detection.
    - `skip_duplicates` boolean: Whether duplicate detection was enabled for this upload
    - `is_duplicate` boolean: Whether this upload was identified as a duplicate of an existing file. If true: - duplicate_of_upload_id contains the original upload - presigned_url will be null (no upload needed) - You can use the original upload's S3 object. This saves bandwidth and storage costs.
    - `duplicate_of_upload_id` string, nullable: If skip_duplicates=true and duplicate found, this is the original upload_id. The response will reference the existing upload instead of creating a new one.
    - `skipped_unique_key` boolean: Whether this upload was skipped because the unique key already exists in the bucket. If true: - existing_object_id contains the ID of the existing object - presigned_url will be null (no upload needed) - No S3 upload is required This saves bandwidth and prevents duplicate objects.
    - `existing_object_id` string, nullable: If skipped_unique_key=true, this is the object_id of the existing object that has the same unique key values. The upload was skipped to prevent duplicates.
    - `message` string, nullable: Human-readable message about the upload. When status is FAILED, the reason it failed (for example it was not confirmed before expires_at, or its confirmation did not finish). Also provided when is_duplicate=true or other special conditions. Example: 'File already exists with the same content hash. No upload needed - returning existing upload.'
    - `created_at` string, date-time, required: When this upload record was created (ISO 8601 format)
    - `expires_at` string, date-time, required: When the presigned URL expires (ISO 8601 format). After this time: - The presigned URL cannot be used - Upload status will be marked as FAILED if not completed - The upload record will be auto-deleted 30 days later (MongoDB TTL)
    - `completed_at` string, date-time, nullable: When the upload was completed and verified (ISO 8601 format)
    - `verified_at` string, date-time, nullable: When S3 object existence was verified (ISO 8601 format)
    - `etag` string, nullable: S3 ETag from the uploaded object (set during confirmation)
    - `object_id` string, nullable: Created bucket object ID (if create_object_on_confirm was true)
    - `task_id` string, nullable: Task ID for async confirmation (if processed asynchronously)
  - `total` integer, required: Total number of uploads created

## Other responses

- `400`: Bad Request
- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not Found
- `422`: Unprocessable Entity
- `429`: Too Many Requests
- `500`: Internal Server Error
- `503`: Service Unavailable

## Changes

- **2026-10-09** `61a175e8eb9a`: 1 warning, 5 info
  - removed the optional property `detail` from the response with the `422` status
  - added the non-success response with the status `429`
  - added the non-success response with the status `503`
  - added the optional property `success` to the response with the `422` status
  - …2 more
- **2026-08-09** `5d4c905106b4`: 1 info
  - the endpoint scheme security `BearerAuth AND NamespaceHeader` was added to the API
- **2026-08-04** `9603dfdab463`: 1 warning
  - added the new `DEACTIVATED` enum value to the `uploads/items/status` response property for the response status `200`

[Operation history](https://skmtc.dev/mixpeek/apis/mixpeek-api/changes/v1/buckets/:bucket_identifier/uploads/batch/post.md)

---

[API](https://skmtc.dev/mixpeek/apis/mixpeek-api.md) - [All operations](https://skmtc.dev/mixpeek/apis/mixpeek-api/llms.txt) - [OpenAPI document](https://skmtc.dev/mixpeek/apis/mixpeek-api/revisions/790bc16455f6?raw)
