> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dreep.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Presigned Upload

> Get a short-lived URL to upload file bytes straight to storage, bypassing the API.

Use this instead of [Upload Media](/api-reference/upload/upload-media) for large
files and browser uploads — the bytes go directly to storage and never pass
through the Dreep API.

The flow is three steps:

1. `POST /api/v1/upload/presign` → returns an `id` and an `uploadUrl`.
2. `PUT` the raw file bytes to `uploadUrl`, with a `Content-Type` matching the
   `contentType` you declared.
3. [`POST /api/v1/upload/{id}/confirm`](/api-reference/upload/confirm-upload) to
   finalize the asset.

Until step 3 the asset stays `pending` and is excluded from media listings.

## Destination

The destination folder is named exactly as it is for a direct upload — with
`folder`, `key`, or `folderId` — and missing folders are created for you. When
you pass a `key`, its last segment supplies the filename, so `filename` becomes
optional. See [Folders & Paths](/guides/folders-and-paths).

## Skipping the upload for content Dreep already has

Send `contentHash` (the SHA-256 of the file). If it matches content already in
storage, the response has `"alreadyExists": true` and no `uploadUrl` — skip the
`PUT` and go straight to confirm.

<Warning>
  The `uploadUrl` is short-lived and single-purpose. Generate it server-side and
  hand it to the client immediately; don't cache or reuse it.
</Warning>


## OpenAPI

````yaml POST /api/v1/upload/presign
openapi: 3.0.0
info:
  title: Dreep API
  description: The universal media processing and storage API.
  version: 1.0.0
servers:
  - url: https://api.dreep.cloud
    description: Production
security:
  - ApiKeyAuth: []
paths:
  /api/v1/upload/presign:
    post:
      tags:
        - Upload
      summary: Create Presigned Upload
      description: >
        Start a direct-to-storage upload. Dreep returns a short-lived
        `uploadUrl`; `PUT` the file bytes straight to it, then call [Confirm
        Upload](/api-reference/upload/confirm-upload) with the returned `id` to
        finalize the asset.


        Use this instead of [Upload Media](/api-reference/upload/upload-media)
        for large files or browser uploads — the bytes never pass through the
        Dreep API. The destination folder is resolved exactly as it is for a
        direct upload, including creating missing folders from a `folder` path
        or `key`.


        If `contentHash` matches an asset Dreep already stores, the response
        comes back with `alreadyExists: true` and no `uploadUrl` — skip the
        `PUT` and go straight to confirm.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                filename:
                  type: string
                  description: >
                    Name to store the file under. Optional when `key` is given,
                    since the key's last segment supplies it.
                  example: me.webp
                contentType:
                  type: string
                  example: image/webp
                sizeBytes:
                  type: integer
                  example: 438720
                folder:
                  type: string
                  description: >-
                    Slug path of the destination folder. Missing folders are
                    created.
                  example: avatars/2024
                key:
                  type: string
                  description: Full S3-style object key (folder path + filename).
                  example: avatars/2024/me.webp
                folderId:
                  type: string
                  format: uuid
                  description: UUID of an existing destination folder.
                autoCreateFolders:
                  type: string
                  enum:
                    - 'true'
                    - 'false'
                  default: 'true'
                format:
                  type: string
                  description: Target format to re-encode to on confirm.
                presetKey:
                  type: string
                contentHash:
                  type: string
                  description: >-
                    SHA-256 of the file, used to skip re-uploading content Dreep
                    already has.
              required:
                - contentType
                - sizeBytes
      responses:
        '201':
          description: Presigned upload created
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    format: uuid
                    description: Pass this to the confirm endpoint.
                  uploadUrl:
                    type: string
                    description: >-
                      Short-lived URL to PUT the file bytes to. Absent when
                      `alreadyExists` is true.
                  contentType:
                    type: string
                  alreadyExists:
                    type: boolean
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '404':
          $ref: '#/components/responses/NotFound'
components:
  responses:
    BadRequest:
      description: Bad Request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Unauthorized
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    PaymentRequired:
      description: A plan limit for this feature has been reached
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Not Found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    Error:
      type: object
      properties:
        error:
          type: boolean
          example: true
        message:
          type: string
          example: Invalid request parameters
        code:
          type: string
          example: invalid_request
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer

````