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

# Get a presigned upload URL

> Creates a pending image record and returns a presigned S3 PUT URL.

**Supported formats:** JPEG, PNG, and RAW files — NEF, CR2, CR3, ARW, DNG, ORF, RW2, RAF, PEF, MRW, SRW, RAW.

After receiving the URL, upload the file directly to S3 with a `PUT` request using the
matching `Content-Type` header. No auth header is needed on the S3 upload.

The pipeline (RAW vs JPEG) is chosen automatically from the file extension.

**Project must be in `pending` status.** Uploads are blocked once processing has started or completed.




## OpenAPI

````yaml POST /v1/images/upload-url
openapi: 3.1.0
info:
  title: Fotolabs API
  version: 1.0.0
  description: >
    Programmatic access to the Fotolabs image processing pipeline.


    **Base URL:** `https://api.fotolabs.co` (prod) ·
    `https://api-staging.fotolabs.co` (staging)


    **Authentication:** Pass your API key as a Bearer token on every request.

    ```

    Authorization: Bearer fl_<key>

    ```


    **Typical flow:**

    1. `POST /v1/projects` — create a project

    2. `POST /v1/images/upload-url` + `PUT <uploadUrl>` — repeat for each image
    (no grouping needed)

    3. `POST /v1/projects/{projectId}/process` — process the whole project in
    one call

    4. `GET /v1/projects/{projectId}` — poll until `status` is `completed`;
    response includes all images and their result URLs


    **Project lifecycle:** `pending` → `processing` → `completed`

    - Images can only be uploaded to a `pending` project.

    - Processing can only be triggered on a `pending` project.

    - Once processing starts the project is locked — create a new project for
    additional images.


    **Plans:**

    - `free` — API access blocked.

    - `essential` / `ultimate` (PAYG) — charged once per project at the plan
    rate.

    - `essential_monthly` / `ultimate_monthly` — deducts one listing from your
    monthly quota; overage rate applies if quota is exhausted.
servers:
  - url: https://api.fotolabs.co
    description: Production
  - url: https://api-staging.fotolabs.co
    description: Staging
security:
  - ApiKeyAuth: []
paths:
  /v1/images/upload-url:
    post:
      summary: Get a presigned upload URL
      description: >
        Creates a pending image record and returns a presigned S3 PUT URL.


        **Supported formats:** JPEG, PNG, and RAW files — NEF, CR2, CR3, ARW,
        DNG, ORF, RW2, RAF, PEF, MRW, SRW, RAW.


        After receiving the URL, upload the file directly to S3 with a `PUT`
        request using the

        matching `Content-Type` header. No auth header is needed on the S3
        upload.


        The pipeline (RAW vs JPEG) is chosen automatically from the file
        extension.


        **Project must be in `pending` status.** Uploads are blocked once
        processing has started or completed.
      operationId: getUploadUrl
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - filename
                - projectId
              properties:
                filename:
                  type: string
                  description: >-
                    Original filename including extension. Determines file type
                    and S3 Content-Type.
                  example: Z6A_0235.NEF
                projectId:
                  type: string
                  format: uuid
                  description: Project this image belongs to. Must be in `pending` status.
                  example: ca5f09f7-6838-459c-ac0f-7be03c60de24
            example:
              filename: Z6A_0235.NEF
              projectId: ca5f09f7-6838-459c-ac0f-7be03c60de24
      responses:
        '200':
          description: Presigned URL ready. Upload the file within 1 hour.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadUrlResponse'
              example:
                imageId: f3e088d1-33a5-4227-a2f6-c4f75617f847
                uploadUrl: https://fotolabs-images-staging.s3.amazonaws.com/...
                originalImageUrl: https://d1i1ga8jkrbik1.cloudfront.net/...
                expiresAt: '2026-05-09T19:00:00.000Z'
        '400':
          description: Validation error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                missingFilename:
                  value:
                    error: filename is required
                unsupportedExtension:
                  value:
                    error: 'Unsupported file extension: .bmp'
                missingProject:
                  value:
                    error: projectId is required
        '401':
          description: Invalid or missing API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Project not found or does not belong to your workspace.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: Project not found or access denied
        '409':
          description: Project is not in `pending` status — uploads are locked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                processing:
                  summary: Project is currently processing
                  value:
                    error: >-
                      Project is currently processing. Wait for it to complete
                      before uploading more images.
                completed:
                  summary: Project already processed
                  value:
                    error: >-
                      Project has already been processed. Create a new project
                      to process more images.
components:
  schemas:
    UploadUrlResponse:
      type: object
      required:
        - imageId
        - uploadUrl
        - originalImageUrl
        - expiresAt
      properties:
        imageId:
          type: string
          format: uuid
          example: f3e088d1-33a5-4227-a2f6-c4f75617f847
        uploadUrl:
          type: string
          format: uri
          description: >-
            Presigned S3 PUT URL. Upload the file here with the matching
            Content-Type. Valid for 1 hour.
          example: https://fotolabs-images-staging.s3.amazonaws.com/...
        originalImageUrl:
          type: string
          format: uri
          description: CloudFront URL where the original will be accessible after upload.
          example: https://d1i1ga8jkrbik1.cloudfront.net/...
        expiresAt:
          type: string
          format: date-time
          description: When the presigned URL expires.
          example: '2026-05-09T19:00:00.000Z'
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          example: >-
            No payment method on file. Please add one at fotolabs.co before
            processing images.
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      description: API key with `fl_` prefix, issued from the Fotolabs dashboard

````