---
title: "Reserve up to 20 object uploads under one quota admission lock"
method: POST
path: "/storage-buckets/{bucketId}/upload-batches"
---

# Reserve up to 20 object uploads under one quota admission lock

`POST /storage-buckets/{bucketId}/upload-batches`

## Request

- Base URL: `https://api.eurobase.dev`
- URL: `https://api.eurobase.dev/storage-buckets/{bucketId}/upload-batches`
- Auth: none declared

Each returned short-lived capability writes only its reserved staging object in private Object Storage. Completion separately verifies and publishes each file.

## Path parameters

- `bucketId` string, required

## Request body

- CreateProjectStorageUploadBatchRequest
  - `files` object[], required
    - `objectKey` string, required
    - `contentType` string, required
    - `sizeBytes` integer, required
    - `sha256` string, required

## Response `201`

Ordered upload sessions

- CreateProjectStorageUploadBatchResponse
  - `project` ProjectSummary, required
    - `id` string, required
    - `name` string, nullable
    - `organizationId` string, nullable
    - `projectMembershipId` string, nullable
    - `projectRole` 'owner' | 'admin' | 'contributor' | 'null', nullable
    - `projectSlug` string, nullable
    - `projectHostname` string, nullable
    - `primaryHostname` string, nullable: Official address of the project: its active custom domain whose DNS points at Eurobase, the one that went live first when several do, otherwise projectHostname. projectHostname stays reserved and keeps serving.
    - `deploymentMode` 'runtime' | 'static', required
    - `lifecycleState` string, nullable
    - `deleteRequestedAt` string, date-time, nullable
    - `projectRoute` ProjectRoute, required
      - `status` 'healthy' | 'repairing' | 'degraded' | 'broken' | 'unknown', required
      - `reasonCode` 'none' | 'dns_record_missing' | 'dns_propagating' | 'domain_binding_missing' | 'tls_pending' | 'missing_project_upstream' | 'upstream_unreachable' | 'ingress_unreachable' | 'unmanaged_custom_domain' | 'scw_mutation_failed', required
      - `checkedAt` string, date-time, nullable
      - `lastRepairAt` string, date-time, nullable
      - `lastError` string, nullable
      - `managed` boolean, required
      - `convergenceState` 'recovering' | 'exhausted' | 'ready' | 'null', nullable
      - `upstream` ProjectUpstreamState, required
        - `status` 'healthy' | 'broken' | 'unknown', required
        - `reasonCode` 'none' | 'missing_project_upstream' | 'upstream_unreachable', required
        - `checkedAt` string, date-time, nullable
        - `lastError` string, nullable
    - `releaseUsability` ReleaseUsability, required
      - `status` 'reachable' | 'degraded' | 'blocked' | 'unknown', required
      - `reasonCode` 'none' | 'missing_live_release' | 'access_not_allowed' | 'route_repair_in_progress' | 'route_incident' | 'project_suspended', required
      - `lastLiveDeploymentId` string, nullable, required
    - `projectSummaryStatus` ProjectSummaryStatus
      - `label` string, required
      - `tone` 'neutral' | 'problem' | 'error', required
      - `code` 'setup_required' | 'route_issue' | 'deploy_failed' | 'deploy_unhealthy' | 'live_release_blocked' | 'status_check_delayed' | 'deploy_in_progress' | 'rollback_live' | 'healthy' | 'suspended', required
      - `detailTitle` string, nullable, required
      - `detail` string, nullable, required
    - `servingSuspension` ProjectServingSuspension: An operator suspension of the project's public serving (ADR 0080). Operator notes and evidence are never included.
      - `state` 'suspended', required
      - `reasonCategory` 'phishing' | 'malware' | 'shared_domain_abuse' | 'spam' | 'illegal_content' | 'legal_order' | 'other_policy_violation', required
      - `reasonLabel` string, required
      - `since` string, date-time, nullable, required
      - `supportEmail` string, required
      - `policyUrl` string, required
    - `sourceProfile` ProjectSourceProfileSummary, required
      - `framework` 'next' | 'astro' | 'svelte-kit' | 'nuxt' | 'react-router' | 'vite' | 'hono' | 'nestjs' | 'fastify' | 'express' | 'django' | 'fastapi' | 'flask' | 'laravel' | 'rails' | 'node' | 'python' | 'php' | 'ruby' | 'go' | 'java' | 'dotnet' | 'static', required
      - `frameworkLabel` string, required
      - `outputMode` 'runtime' | 'static', required
    - `sourceLock` ProjectSourceLock: ADR 0090: while locked, no caller can change the project repository, branch, root directory or deployment mode.
      - `locked` boolean, required
      - `lockedAt` string, date-time, nullable, required
  - `bucket` ProjectStorageBucketSummary, required
    - `id` string, required
    - `name` string, required
    - `pathPrefix` string, required
    - `accessMode` 'private' | 'public', required
    - `status` 'provisioning' | 'ready' | 'failed' | 'pending_delete' | 'delete_failed' | 'deleted', required
    - `objectCount` integer, required
    - `readyCount` integer, required
    - `totalBytes` integer, required
    - `createdAt` string, date-time, nullable, required
    - `visibilitySummary` string, required
    - `mimeSummary` string, required
  - `uploads` CreateProjectStorageUploadResponse[], required
    - `project` ProjectSummary, required
      - `id` string, required
      - `name` string, nullable
      - `organizationId` string, nullable
      - `projectMembershipId` string, nullable
      - `projectRole` 'owner' | 'admin' | 'contributor' | 'null', nullable
      - `projectSlug` string, nullable
      - `projectHostname` string, nullable
      - `primaryHostname` string, nullable: Official address of the project: its active custom domain whose DNS points at Eurobase, the one that went live first when several do, otherwise projectHostname. projectHostname stays reserved and keeps serving.
      - `deploymentMode` 'runtime' | 'static', required
      - `lifecycleState` string, nullable
      - `deleteRequestedAt` string, date-time, nullable
      - `projectRoute` ProjectRoute, required
        - `status` 'healthy' | 'repairing' | 'degraded' | 'broken' | 'unknown', required
        - `reasonCode` 'none' | 'dns_record_missing' | 'dns_propagating' | 'domain_binding_missing' | 'tls_pending' | 'missing_project_upstream' | 'upstream_unreachable' | 'ingress_unreachable' | 'unmanaged_custom_domain' | 'scw_mutation_failed', required
        - `checkedAt` string, date-time, nullable
        - `lastRepairAt` string, date-time, nullable
        - `lastError` string, nullable
        - `managed` boolean, required
        - `convergenceState` 'recovering' | 'exhausted' | 'ready' | 'null', nullable
        - `upstream` ProjectUpstreamState, required
          - `status` 'healthy' | 'broken' | 'unknown', required
          - `reasonCode` 'none' | 'missing_project_upstream' | 'upstream_unreachable', required
          - `checkedAt` string, date-time, nullable
          - `lastError` string, nullable
      - `releaseUsability` ReleaseUsability, required
        - `status` 'reachable' | 'degraded' | 'blocked' | 'unknown', required
        - `reasonCode` 'none' | 'missing_live_release' | 'access_not_allowed' | 'route_repair_in_progress' | 'route_incident' | 'project_suspended', required
        - `lastLiveDeploymentId` string, nullable, required
      - `projectSummaryStatus` ProjectSummaryStatus
        - `label` string, required
        - `tone` 'neutral' | 'problem' | 'error', required
        - `code` 'setup_required' | 'route_issue' | 'deploy_failed' | 'deploy_unhealthy' | 'live_release_blocked' | 'status_check_delayed' | 'deploy_in_progress' | 'rollback_live' | 'healthy' | 'suspended', required
        - `detailTitle` string, nullable, required
        - `detail` string, nullable, required
      - `servingSuspension` ProjectServingSuspension: An operator suspension of the project's public serving (ADR 0080). Operator notes and evidence are never included.
        - `state` 'suspended', required
        - `reasonCategory` 'phishing' | 'malware' | 'shared_domain_abuse' | 'spam' | 'illegal_content' | 'legal_order' | 'other_policy_violation', required
        - `reasonLabel` string, required
        - `since` string, date-time, nullable, required
        - `supportEmail` string, required
        - `policyUrl` string, required
      - `sourceProfile` ProjectSourceProfileSummary, required
        - `framework` 'next' | 'astro' | 'svelte-kit' | 'nuxt' | 'react-router' | 'vite' | 'hono' | 'nestjs' | 'fastify' | 'express' | 'django' | 'fastapi' | 'flask' | 'laravel' | 'rails' | 'node' | 'python' | 'php' | 'ruby' | 'go' | 'java' | 'dotnet' | 'static', required
        - `frameworkLabel` string, required
        - `outputMode` 'runtime' | 'static', required
      - `sourceLock` ProjectSourceLock: ADR 0090: while locked, no caller can change the project repository, branch, root directory or deployment mode.
        - `locked` boolean, required
        - `lockedAt` string, date-time, nullable, required
    - `bucket` ProjectStorageBucketSummary
      - `id` string, required
      - `name` string, required
      - `pathPrefix` string, required
      - `accessMode` 'private' | 'public', required
      - `status` 'provisioning' | 'ready' | 'failed' | 'pending_delete' | 'delete_failed' | 'deleted', required
      - `objectCount` integer, required
      - `readyCount` integer, required
      - `totalBytes` integer, required
      - `createdAt` string, date-time, nullable, required
      - `visibilitySummary` string, required
      - `mimeSummary` string, required
    - `snapshot` ProjectStorageSnapshot
      - `bucketCount` integer, required
      - `objectCount` integer, required
      - `readyObjectCount` integer, required
      - `pendingObjectCount` integer, required
      - `failedObjectCount` integer, required
      - `imageObjectCount` integer, required
      - `totalBytes` integer, required
      - `visibilitySummary` string, required
      - `latestObjectUpdatedAt` string, date-time, nullable, required
    - `storageObject` StorageObject, required
      - `id` string, required
      - `organizationId` string, required
      - `projectId` string, required
      - `bucketId` string, nullable
      - `createdByUserId` string, required
      - `logicalKey` string, required
      - `objectKey` string, nullable
      - `contentType` string, nullable
      - `sizeBytes` integer, nullable
      - `visibility` 'private' | 'public', required
      - `uploadStatus` 'pending' | 'ready' | 'failed' | 'deleted', required
      - `preview` object, required
        - `status` 'loading' | 'ready' | 'unavailable' | 'none', required
        - `imageUrl` string, uri-reference, nullable, required
        - `width` integer, nullable, required
        - `height` integer, nullable, required
        - `actions` object, required
          - `retryPreview` boolean, required
      - `productUrl` string, uri, nullable
      - `createdAt` string, date-time, required
      - `updatedAt` string, date-time, required
    - `completionRequired` boolean, required: True only when the original bytes are durable and this upload attempt must resume authenticated completion.
    - `uploadInProgress` boolean, required: True only when the same upload attempt is still processing and authenticated completion may be retried within the bounded client wait.
    - `upload` object, nullable, required
      - `id` string, required
      - `transport` 'object-storage', required
      - `method` 'PUT', required
      - `url` string, uri, required
      - `headers` object, required
      - `expiresAt` string, date-time, required
      - `maxBytes` integer, required

## Other responses

- `400`: Invalid batch, filename, size, MIME, or checksum
- `409`: Bucket is unavailable, object key conflicts, or quota blocks admission
- `503`: Quota evidence or provider upload policy is unavailable

---

[API](https://skmtc.dev/eurobase/apis/eurobase-api.md) · [All operations](https://skmtc.dev/eurobase/apis/eurobase-api/llms.txt) · [OpenAPI document](https://skmtc.dev/eurobase/apis/eurobase-api/revisions/5266a299d18a?raw)
