Skip to content

Bulk Upload Presigned URLs

Summary

POST /documents/upload-bulk prepares direct-upload form posts for multiple files in a single request. The handler validates every requested file first, then generates presigned uploads, reserves storage only for the files that received a presigned form successfully, and finally persists document rows for those successful items.

Business Rules

  • The request is rejected when any file has an invalid route type, invalid content type, or exceeds the configured route max file size.
  • Presigned upload generation is attempted for every validated file before any storage quota is reserved.
  • Only files that receive a valid presigned form count toward storage reservation and document creation.
  • The fallback storage limit currently uses a mocked plan value in the command until the Plans module becomes the source of truth for per-organization quotas.
  • If every presign attempt fails, the endpoint returns STORAGE.PRESIGN_OPERATION_FAILED and does not reserve storage or create documents.
  • Storage usage increment and document persistence happen inside the same transaction so quota reservations roll back automatically if document registration fails.

API Contract

  • Endpoint: POST /documents/upload-bulk
  • Request body: BulkUploadDocumentDto from packages/schemas/src/storage/bulk-upload-document.schema.ts
  • Response body: BulkUploadDocumentResponseDto
  • results includes only successful uploads.
  • Each successful result now includes index, which matches the file position in the original request array.
  • Frontend consumers should treat missing indexes as per-file presign failures.

Failure Modes

  • When the successful presigned files would exceed the org quota, the command returns STORAGE.QUOTA_EXCEEDED even if some presigns were generated already.
  • Failed presign attempts are omitted from the response instead of aborting the whole batch, unless every presign fails.
  • If the storage stats row does not exist yet for the org, the command creates it inside the same transaction before reserving usage.

Change Log

  • 2026-03-27: Changed bulk upload reservation to happen after presign generation, removed the env-based fallback from this flow, and added per-item request indexes to successful results.