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: "support@nhost.io" 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 successfully retrieved" content: application/json: schema: $ref: "#/components/schemas/VersionInformation" default: description: "Error occurred" content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" components: securitySchemes: Authorization: type: http scheme: bearer bearerFormat: JWT description: API key to authorize requests. X-Hasura-Admin-Secret: type: apiKey name: X-Hasura-Admin-Secret in: header description: "Hasura admin secret key for backend/administrative operations." schemas: RFC2822Date: type: string description: "Date in RFC 2822 format" pattern: '^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{1,2} (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} \w+$' example: "Tue, 12 Aug 2025 12:03:50 GMT" x-go-type: Time ErrorResponse: type: object description: "Error information returned by the API." properties: error: type: object additionalProperties: false description: "Error details." properties: message: type: string description: "Human-readable error message." example: "File not found" data: type: object additionalProperties: true description: "Additional data related to the error, if any." required: - message additionalProperties: false ErrorResponseWithProcessedFiles: type: object description: "Error information returned by the API." properties: processedFiles: type: array description: "List of files that were successfully processed before the error occurred." items: $ref: "#/components/schemas/FileMetadata" error: type: object additionalProperties: false description: "Error details." properties: message: type: string description: "Human-readable error message." example: "File not found" data: type: object additionalProperties: true description: "Additional data related to the error, if any." required: - message additionalProperties: false FileMetadata: type: object description: "Comprehensive metadata information about a file in storage." properties: id: type: string description: "Unique identifier for the file." example: "d5e76ceb-77a2-4153-b7da-1f7c115b2ff2" name: type: string description: "Name of the file including extension." example: "profile-picture.jpg" size: description: "Size of the file in bytes." type: integer format: int64 example: 245678 bucketId: type: string description: "ID of the bucket containing the file." example: "users-bucket" etag: type: string description: "Entity tag for cache validation." example: '"a1b2c3d4e5f6"' createdAt: type: string format: date-time description: "Timestamp when the file was created." example: "2023-01-15T12:34:56Z" updatedAt: type: string format: date-time description: "Timestamp when the file was last updated." example: "2023-01-16T09:45:32Z" isUploaded: type: boolean description: "Whether the file has been successfully uploaded." example: true mimeType: type: string description: "MIME type of the file." example: "image/jpeg" uploadedByUserId: type: string description: "ID of the user who uploaded the file." example: "abc123def456" metadata: type: object additionalProperties: true description: "Custom metadata associated with the file." example: { "alt": "Profile picture", "category": "avatar" } required: - id - name - size - bucketId - etag - createdAt - updatedAt - isUploaded - mimeType additionalProperties: false FileSummary: type: object description: "Basic information about a file in storage." properties: id: type: string description: "Unique identifier for the file." example: "d5e76ceb-77a2-4153-b7da-1f7c115b2ff2" name: type: string description: "Name of the file including extension." example: "profile-picture.jpg" bucketId: type: string description: "ID of the bucket containing the file." example: "users-bucket" isUploaded: type: boolean description: "Whether the file has been successfully uploaded." example: true additionalProperties: false required: - id - name - bucketId - isUploaded PresignedURLResponse: type: object description: "Contains a presigned URL for direct file operations." properties: url: type: string description: "The presigned URL for file operations." example: "https://storage.example.com/files/abc123?signature=xyz" expiration: type: integer description: "The time in seconds until the URL expires." example: 3600 required: - url - expiration additionalProperties: false UpdateFileMetadata: type: object description: "Metadata that can be updated for an existing file." properties: name: type: string description: "New name to assign to the file." example: "renamed-file.jpg" metadata: type: object additionalProperties: true description: "Updated custom metadata to associate with the file." example: { "alt": "Updated image description", "category": "profile" } additionalProperties: false UploadFileMetadata: type: object description: "Metadata provided when uploading a new file." properties: id: type: string description: "Optional custom ID for the file. If not provided, a UUID will be generated." example: "custom-id-123" name: type: string description: "Name to assign to the file. If not provided, the original filename will be used." example: "custom-filename.png" metadata: type: object additionalProperties: true description: "Custom metadata to associate with the file." example: { "alt": "Custom image", "category": "document" } additionalProperties: false VersionInformation: type: object description: "Contains version information about the storage service." additionalProperties: false properties: buildVersion: type: string description: "The version number of the storage service build." example: "1.2.3" required: - buildVersion OutputImageFormat: type: string description: "Output format for image files. Use 'auto' for content negotiation based on Accept header" default: same enum: - auto - same - jpeg - webp - png - avif example: same