Nhost Storage API

S3-compatible file storage service with GraphQL metadata management, CDN delivery across 80+ locations, image transformation, antivirus scanning, and pre-signed URL generation. Permissions enforced via Hasura's role-based access control.

OpenAPI Specification

nhost-storage-openapi.yml Raw ↑
openapi: "3.0.0"
info:
  version: 1.0.0
  title: "Nhost Storage API"
  description: "Nhost Storage API - A service for managing and serving files with powerful access control capabilities"
  license:
    name: "Apache License 2.0"
    url: "https://www.apache.org/licenses/LICENSE-2.0"
  contact:
    name: "Nhost Support"
    email: "[email protected]"
    url: "https://nhost.io"

servers:
  - url: "https://{subdomain}.storage.{region}.nhost.run/v1"
    description: "Nhost Storage API Server"

tags:
  - name: documentation
    description: API documentation
  - name: excludeme
    description: Internal operations excluded from public docs
  - name: files
    description: File management operations
  - name: operations
    description: Administrative operations
  - name: storage
    description: Storage operations and presigned URLs
  - name: system
    description: System information

paths:
  /files:
    post:
      summary: "Upload files"
      description: "Upload one or more files to a specified bucket. Supports batch uploading with optional custom metadata for each file. If uploading multiple files, either provide metadata for all files or none."
      operationId: uploadFiles
      tags:
        - files
      security:
        - Authorization: []
      requestBody:
        description: "File upload data including files and optional metadata"
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                bucket-id:
                  type: string
                  description: "Target bucket identifier where files will be stored."
                  example: "user-uploads"
                metadata[]:
                  type: array
                  description: "Optional custom metadata for each uploaded file. Must match the order of the file[] array."
                  items:
                    $ref: "#/components/schemas/UploadFileMetadata"
                file[]:
                  description: "Array of files to upload."
                  type: array
                  items:
                    type: string
                    format: binary
              required:
                - file[]
      responses:
        "201":
          description: "Files successfully uploaded"
          content:
            application/json:
              schema:
                type: object
                properties:
                  processedFiles:
                    type: array
                    description: "List of successfully processed files with their metadata."
                    items:
                      $ref: "#/components/schemas/FileMetadata"
                required:
                  - processedFiles
        default:
          description: "Error occurred during upload"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponseWithProcessedFiles"

  /files/{id}:
    delete:
      summary: "Delete file"
      description: "Permanently delete a file from storage. This removes both the file content and its associated metadata."
      operationId: deleteFile
      tags:
        - files
      security:
        - Authorization: []
      parameters:
        - name: id
          required: true
          in: path
          description: "Unique identifier of the file to delete"
          schema:
            type: string
      responses:
        "204":
          description: "File successfully deleted"
        default:
          description: "Error occurred during file deletion"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

    get:
      summary: "Download file"
      description: "Retrieve and download the complete file content. Supports conditional requests, image transformations, and range requests for partial downloads."
      operationId: getFile
      tags:
        - files
      security:
        - Authorization: []
      parameters:
        - name: id
          required: true
          in: path
          description: "Unique identifier of the file to download"
          schema:
            type: string
        - name: if-match
          description: "Only return the file if the current ETag matches one of the values provided"
          in: header
          schema:
            type: string
        - name: if-none-match
          description: "Only return the file if the current ETag does not match any of the values provided"
          in: header
          schema:
            type: string
        - name: if-modified-since
          description: "Only return the file if it has been modified after the given date"
          in: header
          schema:
            $ref: '#/components/schemas/RFC2822Date'
        - name: if-unmodified-since
          description: "Only return the file if it has not been modified after the given date"
          in: header
          schema:
            $ref: '#/components/schemas/RFC2822Date'
        - name: q
          description: "Image quality (1-100). Only applies to JPEG, WebP and PNG files"
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
        - name: h
          description: "Maximum height to resize image to while maintaining aspect ratio. Only applies to image files"
          in: query
          schema:
            type: integer
            minimum: 1
        - name: w
          description: "Maximum width to resize image to while maintaining aspect ratio. Only applies to image files"
          in: query
          schema:
            type: integer
            minimum: 1
        - name: b
          description: "Blur the image using this sigma value. Only applies to image files"
          in: query
          schema:
            type: number
            minimum: 0
        - name: f
          description: "Output format for image files. Use 'auto' for content negotiation based on Accept header"
          in: query
          schema:
            $ref: '#/components/schemas/OutputImageFormat'
        - name: Range
          description: "Range of bytes to retrieve from the file. Format: bytes=start-end"
          in: header
          schema:
            type: string
            pattern: '^bytes=(\d+-\d*|\d*-\d+)(,(\d+-\d*|\d*-\d+))*$'
      responses:
        "200":
          description: "File content retrieved successfully"
          headers:
            Cache-Control:
              description: "Directives for caching mechanisms"
              schema:
                type: string
            Content-Type:
              description: "MIME type of the file"
              schema:
                type: string
            Etag:
              description: "Entity tag for cache validation"
              schema:
                type: string
            Content-Disposition:
              description: "Indicates if the content should be displayed inline or as an attachment"
              schema:
                type: string
            Last-Modified:
              description: "Date and time the file was last modified"
              schema:
                type: string
                format: date-time
            Surrogate-Key:
              description: "Cache key for surrogate caching"
              schema:
                type: string
            Surrogate-Control:
              description: "Cache control directives for surrogate caching"
              schema:
                type: string
            Accept-Ranges:
              description: Always set to bytes. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Ranges
              schema:
                type: string
          content:
            application/octet-stream: {}
        "206":
          description: "Partial file content retrieved successfully"
          headers:
            Cache-Control:
              description: "Directives for caching mechanisms"
              schema:
                type: string
            Content-Type:
              description: "MIME type of the file"
              schema:
                type: string
            Content-Range:
              description: "Range of bytes returned in the response"
              schema:
                type: string
            Etag:
              description: "Entity tag for cache validation"
              schema:
                type: string
            Content-Disposition:
              description: "Indicates if the content should be displayed inline or as an attachment"
              schema:
                type: string
            Last-Modified:
              description: "Date and time the file was last modified"
              schema:
                type: string
                format: date-time
            Surrogate-Key:
              description: "Cache key for surrogate caching"
              schema:
                type: string
            Surrogate-Control:
              description: "Cache control directives for surrogate caching"
              schema:
                type: string
          content:
            application/octet-stream: {}
        "304":
          description: "File not modified since the condition specified in If-Modified-Since or If-None-Match headers"
          headers:
            Cache-Control:
              description: "Directives for caching mechanisms"
              schema:
                type: string
            Etag:
              description: "Entity tag for cache validation"
              schema:
                type: string
            Surrogate-Control:
              description: "Cache control directives for surrogate caching"
              schema:
                type: string
        "412":
          description: "Precondition failed for conditional request headers (If-Match, If-Unmodified-Since, If-None-Match)"
          headers:
            Cache-Control:
              description: "Directives for caching mechanisms"
              schema:
                type: string
            Etag:
              description: "Entity tag for cache validation"
              schema:
                type: string
            Surrogate-Control:
              description: "Cache control directives for surrogate caching"
              schema:
                type: string
        default:
          description: "Error occurred"
          headers:
            X-Error:
              description: "Error message details"
              schema:
                type: string

    head:
      summary: "Check file information"
      description: "Retrieve file metadata headers without downloading the file content. Supports conditional requests and provides caching information."
      operationId: getFileMetadataHeaders
      tags:
        - files
      security:
        - Authorization: []
      parameters:
        - name: id
          required: true
          in: path
          description: "Unique identifier of the file to check"
          schema:
            type: string
        - name: if-match
          description: "Only return the file if the current ETag matches one of the values provided"
          in: header
          schema:
            type: string
        - name: if-none-match
          description: "Only return the file if the current ETag does not match any of the values provided"
          in: header
          schema:
            type: string
        - name: if-modified-since
          description: "Only return the file if it has been modified after the given date"
          in: header
          schema:
            $ref: '#/components/schemas/RFC2822Date'
        - name: if-unmodified-since
          description: "Only return the file if it has not been modified after the given date"
          in: header
          schema:
            $ref: '#/components/schemas/RFC2822Date'
        - name: q
          description: "Image quality (1-100). Only applies to JPEG, WebP and PNG files"
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
        - name: h
          description: "Maximum height to resize image to while maintaining aspect ratio. Only applies to image files"
          in: query
          schema:
            type: integer
            minimum: 1
        - name: w
          description: "Maximum width to resize image to while maintaining aspect ratio. Only applies to image files"
          in: query
          schema:
            type: integer
            minimum: 1
        - name: b
          description: "Blur the image using this sigma value. Only applies to image files"
          in: query
          schema:
            type: number
            minimum: 0
        - name: f
          description: "Output format for image files. Use 'auto' for content negotiation based on Accept header"
          in: query
          schema:
            $ref: '#/components/schemas/OutputImageFormat'

      responses:
        "200":
          description: "File information headers retrieved successfully"
          headers:
            Cache-Control:
              description: "Directives for caching mechanisms"
              schema:
                type: string
            Content-Type:
              description: "MIME type of the file"
              schema:
                type: string
            Content-Length:
              description: "Size of the file in bytes"
              schema:
                type: integer
            Etag:
              description: "Entity tag for cache validation"
              schema:
                type: string
            Content-Disposition:
              description: "Indicates if the content should be displayed inline or as an attachment"
              schema:
                type: string
            Last-Modified:
              description: "Date and time the file was last modified"
              schema:
                type: string
                format: date-time
            Accept-Ranges:
              description: "Always set to bytes. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Ranges"
              schema:
                type: string
            Surrogate-Key:
              description: "Cache key for surrogate caching"
              schema:
                type: string
            Surrogate-Control:
              description: "Cache control directives for surrogate caching"
              schema:
                type: string
        "304":
          description: "File not modified since the condition specified in If-Modified-Since or If-None-Match headers"
          headers:
            Cache-Control:
              description: "Directives for caching mechanisms"
              schema:
                type: string
            Etag:
              description: "Entity tag for cache validation"
              schema:
                type: string
            Surrogate-Control:
              description: "Cache control directives for surrogate caching"
              schema:
                type: string
        "412":
          description: "Precondition failed for conditional request headers (If-Match, If-Unmodified-Since)"
          headers:
            Cache-Control:
              description: "Directives for caching mechanisms"
              schema:
                type: string
            Etag:
              description: "Entity tag for cache validation"
              schema:
                type: string
            Surrogate-Control:
              description: "Cache control directives for surrogate caching"
              schema:
                type: string
        default:
          description: "Error occurred"
          headers:
            X-Error:
              description: "Error message details"
              schema:
                type: string

    put:
      summary: "Replace file"
      description: |
        Replace an existing file with new content while preserving the file ID. The operation follows these steps:
        1. The isUploaded flag is set to false to mark the file as being updated
        2. The file content is replaced in the storage backend
        3. File metadata is updated (size, mime-type, isUploaded, etc.)

        Each step is atomic, but if a step fails, previous steps will not be automatically rolled back.
      operationId: replaceFile
      tags:
        - files
      security:
        - Authorization: []
      parameters:
        - name: id
          required: true
          in: path
          description: "Unique identifier of the file to replace"
          schema:
            type: string
      requestBody:
        description: "File replacement data including new file content and optional metadata"
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                metadata:
                  $ref: "#/components/schemas/UpdateFileMetadata"
                file:
                  description: "New file content to replace the existing file"
                  type: string
                  format: binary
      responses:
        "200":
          description: "File successfully replaced"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FileMetadata"
        default:
          description: "Error occurred during file replacement"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /files/{id}/presignedurl:
    get:
      summary: Retrieve presigned URL to retrieve the file
      operationId: getFilePresignedURL
      description: |
        Retrieve presigned URL to retrieve the file. Expiration of the URL is
        determined by bucket configuration
      tags:
        - storage
      security:
        - Authorization: []
      parameters:
        - name: id
          required: true
          in: path
          description: "Unique identifier of the file"
          schema:
            type: string
      responses:
        "200":
          description: File gathered successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PresignedURLResponse"

        default:
          description: Some error occurred
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /files/{id}/presignedurl/contents:
    get:
      summary: Retrieve contents of file
      operationId: getFileWithPresignedURL
      description: Retrieve contents of file
      tags:
        - storage
        - excludeme
      security:
        - Authorization: []
      parameters:
        - name: id
          required: true
          in: path
          description: "Unique identifier of the file"
          schema:
            type: string
        - name: X-Amz-Algorithm
          description: Use presignedurl endpoint to generate this automatically
          required: true
          in: query
          schema:
            type: string
        - name: X-Amz-Credential
          description: Use presignedurl endpoint to generate this automatically
          required: true
          in: query
          schema:
            type: string
        - name: X-Amz-Date
          description: Use presignedurl endpoint to generate this automatically
          required: true
          in: query
          schema:
            type: string
        - name: X-Amz-Expires
          description: Use presignedurl endpoint to generate this automatically
          required: true
          in: query
          schema:
            type: string
        - name: X-Amz-Signature
          description: Use presignedurl endpoint to generate this automatically
          required: true
          in: query
          schema:
            type: string
        - name: X-Amz-SignedHeaders
          description: Use presignedurl endpoint to generate this automatically
          required: true
          in: query
          schema:
            type: string
        - name: X-Amz-Checksum-Mode
          description: Use presignedurl endpoint to generate this automatically
          required: true
          in: query
          schema:
            type: string
        - name: X-Amz-Security-Token
          description: Use presignedurl endpoint to generate this automatically
          required: false
          in: query
          schema:
            type: string
        - name: x-id
          description: Use presignedurl endpoint to generate this automatically
          required: true
          in: query
          schema:
            type: string
        - name: if-match
          description: "Only return the file if the current ETag matches one of the values provided"
          in: header
          schema:
            type: string
        - name: if-none-match
          description: "Only return the file if the current ETag does not match any of the values provided"
          in: header
          schema:
            type: string
        - name: if-modified-since
          description: "Only return the file if it has been modified after the given date"
          in: header
          schema:
            $ref: '#/components/schemas/RFC2822Date'
        - name: if-unmodified-since
          description: "Only return the file if it has not been modified after the given date"
          in: header
          schema:
            $ref: '#/components/schemas/RFC2822Date'
        - name: q
          description: "Image quality (1-100). Only applies to JPEG, WebP and PNG files"
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
        - name: h
          description: "Maximum height to resize image to while maintaining aspect ratio. Only applies to image files"
          in: query
          schema:
            type: integer
            minimum: 1
        - name: w
          description: "Maximum width to resize image to while maintaining aspect ratio. Only applies to image files"
          in: query
          schema:
            type: integer
            minimum: 1
        - name: b
          description: "Blur the image using this sigma value. Only applies to image files"
          in: query
          schema:
            type: number
            minimum: 0
        - name: f
          description: "Output format for image files. Use 'auto' for content negotiation based on Accept header"
          in: query
          schema:
            $ref: '#/components/schemas/OutputImageFormat'
        - name: Range
          description: "Range of bytes to retrieve from the file. Format: bytes=start-end"
          in: header
          schema:
            type: string
            pattern: '^bytes=(\d+-\d*|\d*-\d+)(,(\d+-\d*|\d*-\d+))*$'
      responses:
        "200":
          description: "File content retrieved successfully"
          headers:
            Cache-Control:
              description: "Directives for caching mechanisms"
              schema:
                type: string
            Content-Type:
              description: "MIME type of the file"
              schema:
                type: string
            Etag:
              description: "Entity tag for cache validation"
              schema:
                type: string
            Content-Disposition:
              description: "Indicates if the content should be displayed inline or as an attachment"
              schema:
                type: string
            Last-Modified:
              description: "Date and time the file was last modified"
              schema:
                type: string
                format: date-time
            Surrogate-Key:
              description: "Cache key for surrogate caching"
              schema:
                type: string
            Surrogate-Control:
              description: "Cache control directives for surrogate caching"
              schema:
                type: string
            Accept-Ranges:
              description: Always set to bytes. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Ranges
              schema:
                type: string
          content:
            application/octet-stream: {}
        "206":
          description: "Partial file content retrieved successfully"
          headers:
            Cache-Control:
              description: "Directives for caching mechanisms"
              schema:
                type: string
            Content-Type:
              description: "MIME type of the file"
              schema:
                type: string
            Content-Range:
              description: "Range of bytes returned in the response"
              schema:
                type: string
            Etag:
              description: "Entity tag for cache validation"
              schema:
                type: string
            Content-Disposition:
              description: "Indicates if the content should be displayed inline or as an attachment"
              schema:
                type: string
            Last-Modified:
              description: "Date and time the file was last modified"
              schema:
                type: string
                format: date-time
            Surrogate-Key:
              description: "Cache key for surrogate caching"
              schema:
                type: string
            Surrogate-Control:
              description: "Cache control directives for surrogate caching"
              schema:
                type: string
          content:
            application/octet-stream: {}
        "304":
          description: "File not modified since the condition specified in If-Modified-Since or If-None-Match headers"
          headers:
            Cache-Control:
              description: "Directives for caching mechanisms"
              schema:
                type: string
            Etag:
              description: "Entity tag for cache validation"
              schema:
                type: string
            Surrogate-Control:
              description: "Cache control directives for surrogate caching"
              schema:
                type: string
        "412":
          description: "Precondition failed for conditional request headers (If-Match, If-Unmodified-Since, If-None-Match)"
          headers:
            Cache-Control:
              description: "Directives for caching mechanisms"
              schema:
                type: string
            Etag:
              description: "Entity tag for cache validation"
              schema:
                type: string
            Surrogate-Control:
              description: "Cache control directives for surrogate caching"
              schema:
                type: string
        default:
          description: "Error occurred"
          headers:
            X-Error:
              description: "Error message details"
              schema:
                type: string

  /openapi.yaml:
    get:
      summary: "Get OpenAPI specification"
      description: "Returns the OpenAPI schema definition for this API, allowing clients to understand the available endpoints and models."
      operationId: getOpenAPISpec
      tags:
        - documentation
        - excludeme
      responses:
        "200":
          description: "OpenAPI schema definition"
          content:
            application/x-yaml:
              schema:
                type: object
        default:
          description: "Error occurred"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /ops/delete-broken-metadata:
    post:
      summary: Delete broken metadata
      operationId: deleteBrokenMetadata
      description: Broken metadata is defined as metadata that has isUploaded = true but there is no file in the storage matching it. This is an admin operation that requires the Hasura admin secret.
      tags:
        - operations
      security:
        - X-Hasura-Admin-Secret: []
      responses:
        "200":
          description: Successfully deleted broken metadata
          content:
            application/json:
              schema:
                type: object
                properties:
                  metadata:
                    type: array
                    items:
                      $ref: "#/components/schemas/FileSummary"
        default:
          description: En error occured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /ops/delete-orphans:
    post:
      summary: Deletes orphaned files
      operationId: deleteOrphanedFiles
      description: Orphaned files are files that are present in the storage but have no associated metadata. This is an admin operation that requires the Hasura admin secret.
      tags:
        - operations
      security:
        - X-Hasura-Admin-Secret: []
      responses:
        "200":
          description: Successfully deleted orphaned files
          content:
            application/json:
              schema:
                type: object
                properties:
                  files:
                    type: array
                    items:
                      type: string
        default:
          description: En error occured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /ops/list-broken-metadata:
    post:
      summary: Lists broken metadata
      operationId: listBrokenMetadata
      description: Broken metadata is defined as metadata that has isUploaded = true but there is no file in the storage matching it. This is an admin operation that requires the Hasura admin secret.
      tags:
        - operations
      security:
        - X-Hasura-Admin-Secret: []
      responses:
        "200":
          description: Successfully computed broken metadata
          content:
            application/json:
              schema:
                type: object
                properties:
                  metadata:
                    type: array
                    items:
                      $ref: "#/components/schemas/FileSummary"
        default:
          description: En error occured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /ops/list-not-uploaded:
    post:
      summary: Lists files that haven't been uploaded
      operationId: listFilesNotUploaded
      description: That is, metadata that has isUploaded = false. This is an admin operation that requires the Hasura admin secret.
      tags:
        - operations
      security:
        - X-Hasura-Admin-Secret: []
      responses:
        "200":
          description: Successfully checked files not uploaded
          content:
            application/json:
              schema:
                type: object
                properties:
                  metadata:
                    type: array
                    items:
                      $ref: "#/components/schemas/FileSummary"
        default:
          description: En error occured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /ops/list-orphans:
    post:
      summary: Lists orphaned files
      operationId: listOrphanedFiles
      description: Orphaned files are files that are present in the storage but have no associated metadata. This is an admin operation that requires the Hasura admin secret.
      tags:
        - operations
      security:
        - X-Hasura-Admin-Secret: []
      responses:
        "200":
          description: Successfully computed orphaned files
          content:
            application/json:
              schema:
                type: object
                properties:
                  files:
                    type: array
                    items:
                      type: string
        default:
          description: En error occured
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"

  /version:
    get:
      summary: "Get service version information"
      description: "Retrieves build and version information about the storage service. Useful for monitoring and debugging."
      operationId: getVersion
      tags:
        - system
      responses:
        "200":
          description: "Version information successfu

# --- truncated at 32 KB (39 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/nhost/refs/heads/main/openapi/nhost-storage-openapi.yml