{
  "openapi": "3.1.0",
  "info": {
    "title": "Juicebox API",
    "description": "Public HTTP API for Juicebox file uploads. You can upload files, check their status, renew or delete them, and look up your owned files. Every endpoint returns JSON unless you send an Accept: text/html header, in which case some return HTML.",
    "license": {
      "name": "MIT"
    },
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "/",
      "description": "Relative to the server root"
    }
  ],
  "paths": {
    "/api/admin/announcement": {
      "get": {
        "tags": [
          "Admin"
        ],
        "operationId": "get_announcement_handler",
        "responses": {
          "200": {
            "description": "Current announcement",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnnouncementResponse"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated"
          }
        },
        "security": []
      },
      "put": {
        "tags": [
          "Admin"
        ],
        "operationId": "put_announcement_handler",
        "requestBody": {
          "description": "Announcement to set",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnnouncementPayload"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Announcement updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnnouncementResponse"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated"
          }
        },
        "security": []
      }
    },
    "/api/admin/ban/{ip}": {
      "delete": {
        "tags": [
          "Admin"
        ],
        "operationId": "unban_ip_handler",
        "parameters": [
          {
            "name": "ip",
            "in": "path",
            "description": "The IP address to unban",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "IP unbanned"
          },
          "401": {
            "description": "Not authenticated"
          },
          "404": {
            "description": "IP not found in ban list"
          }
        },
        "security": []
      }
    },
    "/api/admin/bans": {
      "get": {
        "tags": [
          "Admin"
        ],
        "operationId": "list_bans_handler",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Search query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Sort column",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dir",
            "in": "query",
            "description": "Sort direction",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Offset",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Limit",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated ban list"
          },
          "401": {
            "description": "Not authenticated"
          }
        },
        "security": []
      },
      "post": {
        "tags": [
          "Admin"
        ],
        "operationId": "ban_ip_handler",
        "requestBody": {
          "description": "IP address and optional reason to ban",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BanIpPayload"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "IP banned"
          },
          "400": {
            "description": "IP is required"
          },
          "401": {
            "description": "Not authenticated"
          }
        },
        "security": []
      }
    },
    "/api/admin/check": {
      "get": {
        "tags": [
          "Admin"
        ],
        "operationId": "check_handler",
        "responses": {
          "200": {
            "description": "JWT is valid",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckResponse"
                }
              }
            }
          },
          "401": {
            "description": "Not authenticated or invalid token"
          }
        },
        "security": []
      }
    },
    "/api/admin/file/{id}": {
      "delete": {
        "tags": [
          "Admin"
        ],
        "operationId": "delete_file_handler",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The file ID to delete",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "File deleted"
          },
          "401": {
            "description": "Not authenticated"
          },
          "404": {
            "description": "File not found"
          }
        },
        "security": []
      }
    },
    "/api/admin/files": {
      "get": {
        "tags": [
          "Admin"
        ],
        "operationId": "files_handler",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Search query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Sort column",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dir",
            "in": "query",
            "description": "Sort direction",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Offset",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Limit",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated file list"
          },
          "401": {
            "description": "Not authenticated"
          }
        },
        "security": []
      }
    },
    "/api/admin/login": {
      "post": {
        "tags": [
          "Admin"
        ],
        "operationId": "login_handler",
        "requestBody": {
          "description": "Admin username and password",
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "username",
                  "password"
                ],
                "properties": {
                  "password": {
                    "type": "string"
                  },
                  "username": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Login successful, sets JWT cookie",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginResponse"
                }
              }
            }
          },
          "401": {
            "description": "Invalid credentials"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        }
      }
    },
    "/api/admin/logout": {
      "post": {
        "tags": [
          "Admin"
        ],
        "operationId": "logout_handler",
        "responses": {
          "302": {
            "description": "Redirects to /admin/login and clears the JWT cookie"
          }
        },
        "security": []
      }
    },
    "/api/admin/report/{id}": {
      "delete": {
        "tags": [
          "Admin"
        ],
        "operationId": "delete_report_handler",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The report ID to delete",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Report deleted"
          },
          "401": {
            "description": "Not authenticated"
          },
          "404": {
            "description": "Report not found"
          }
        },
        "security": []
      }
    },
    "/api/admin/reports": {
      "get": {
        "tags": [
          "Admin"
        ],
        "operationId": "reports_handler",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Search query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Sort column",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dir",
            "in": "query",
            "description": "Sort direction",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Offset",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Limit",
            "required": true,
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated report list"
          },
          "401": {
            "description": "Not authenticated"
          }
        },
        "security": []
      }
    },
    "/api/announcement": {
      "get": {
        "tags": [
          "General"
        ],
        "operationId": "announcement_handler",
        "responses": {
          "200": {
            "description": "Active site announcement, or null",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/config": {
      "get": {
        "tags": [
          "General"
        ],
        "operationId": "config_handler",
        "responses": {
          "200": {
            "description": "Public server configuration",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/feedback": {
      "post": {
        "tags": [
          "General"
        ],
        "summary": "POST /api/feedback - accept a feedback form submission and redirect to confirmation.",
        "operationId": "feedback_submit_handler",
        "requestBody": {
          "description": "Feedback form fields",
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "303": {
            "description": "Redirect to feedback page with submitted flag"
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "tags": [
          "General"
        ],
        "operationId": "health_handler",
        "responses": {
          "200": {
            "description": "Service health status (juicehost is always unknown since we check passively)",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          }
        }
      }
    },
    "/api/owned-files": {
      "post": {
        "tags": [
          "Files"
        ],
        "operationId": "owned_files_handler",
        "requestBody": {
          "description": "List of file IDs and delete tokens you own",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OwnedFilesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "List of files you own that havent expired yet",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OwnedFilesResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/register": {
      "post": {
        "tags": [
          "General"
        ],
        "operationId": "register_handler",
        "requestBody": {
          "description": "File metadata for juicehost to register",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "File registered, returns delete token and expiry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegisterResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid file ID format"
          },
          "403": {
            "description": "Invalid or missing x-juicehost-api-key header"
          }
        }
      }
    },
    "/api/report": {
      "post": {
        "tags": [
          "General"
        ],
        "summary": "POST /report - accept a form report submission, store in DB, notify admins, and redirect back.",
        "operationId": "report_submit_handler",
        "requestBody": {
          "description": "Report form fields",
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "details": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "file_url": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "303": {
            "description": "Redirects to /report?submitted=1"
          }
        }
      }
    },
    "/api/tus": {
      "post": {
        "tags": [
          "Uploads"
        ],
        "operationId": "create_upload_handler",
        "requestBody": {
          "description": "Empty body. File metadata is sent via headers: Upload-Length, Upload-Metadata (base64-encoded pairs)",
          "content": {
            "application/json": {}
          }
        },
        "responses": {
          "201": {
            "description": "TUS upload session created",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "URL of the created upload resource"
              },
              "Tus-Resumable": {
                "schema": {
                  "type": "string"
                },
                "description": "TUS protocol version"
              }
            }
          },
          "413": {
            "description": "File too large"
          },
          "429": {
            "description": "Rate limit exceeded or too many TUS sessions"
          }
        }
      },
      "options": {
        "tags": [
          "Uploads"
        ],
        "operationId": "options_handler",
        "responses": {
          "204": {
            "description": "TUS capabilities",
            "headers": {
              "Tus-Extension": {
                "schema": {
                  "type": "string"
                },
                "description": "Supported TUS extensions"
              },
              "Tus-Resumable": {
                "schema": {
                  "type": "string"
                },
                "description": "TUS protocol version"
              },
              "Tus-Version": {
                "schema": {
                  "type": "string"
                },
                "description": "Supported TUS version"
              }
            }
          }
        }
      }
    },
    "/api/tus/{id}": {
      "get": {
        "tags": [
          "Uploads"
        ],
        "operationId": "get_upload_handler",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The TUS upload session ID",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Upload progress",
            "headers": {
              "Tus-Resumable": {
                "schema": {
                  "type": "string"
                },
                "description": "TUS protocol version"
              },
              "Upload-Length": {
                "schema": {
                  "type": "string"
                },
                "description": "Total expected size"
              },
              "Upload-Offset": {
                "schema": {
                  "type": "string"
                },
                "description": "Current byte offset"
              }
            }
          },
          "404": {
            "description": "TUS session not found"
          }
        }
      },
      "delete": {
        "tags": [
          "Uploads"
        ],
        "operationId": "delete_upload_handler",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The TUS upload session ID",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "TUS session deleted and temp file removed"
          },
          "404": {
            "description": "TUS session not found"
          }
        }
      },
      "patch": {
        "tags": [
          "Uploads"
        ],
        "operationId": "patch_upload_handler",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The TUS upload session ID",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "Binary chunk of file data",
          "content": {
            "application/offset+octet-stream": {}
          }
        },
        "responses": {
          "200": {
            "description": "Upload complete, file is now live",
            "content": {
              "application/json": {
                "schema": {}
              }
            }
          },
          "204": {
            "description": "Chunk accepted, upload in progress",
            "headers": {
              "Tus-Resumable": {
                "schema": {
                  "type": "string"
                },
                "description": "TUS protocol version"
              },
              "Upload-Offset": {
                "schema": {
                  "type": "string"
                },
                "description": "New byte offset after this chunk"
              }
            }
          },
          "404": {
            "description": "TUS session not found"
          },
          "409": {
            "description": "Upload offset mismatch"
          },
          "413": {
            "description": "Chunk exceeds file size limit"
          }
        }
      }
    },
    "/file/{id}": {
      "delete": {
        "tags": [
          "Files"
        ],
        "operationId": "delete_file_handler",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The file ID",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "Set the x-delete-token header instead of a body",
          "content": {
            "application/json": {
              "schema": {}
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "File deleted"
          },
          "403": {
            "description": "Invalid delete token"
          },
          "404": {
            "description": "File not found"
          }
        }
      }
    },
    "/file/{id}/delete": {
      "post": {
        "tags": [
          "Files"
        ],
        "summary": "POST /file/:id/delete - no-JS form deletion that redirects to /files.",
        "operationId": "delete_file_form_handler",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The file ID",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "Form with a 'token' field containing the delete token",
          "content": {
            "application/x-www-form-urlencoded": {}
          }
        },
        "responses": {
          "302": {
            "description": "Redirects to /files.html?deleted={id}"
          },
          "403": {
            "description": "Invalid delete token"
          },
          "404": {
            "description": "File not found"
          }
        }
      }
    },
    "/file/{id}/info": {
      "get": {
        "tags": [
          "Files"
        ],
        "operationId": "file_info_handler",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The file ID",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "File metadata",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileInfoResponse"
                }
              }
            }
          },
          "404": {
            "description": "File not found"
          },
          "410": {
            "description": "File has expired"
          }
        }
      }
    },
    "/file/{id}/renew": {
      "post": {
        "tags": [
          "Files"
        ],
        "summary": "Rotate a file's ID to get a new public URL.\nYou prove ownership with the delete token. The DB updates and juicehost\nrenames the file so it stays reachable at the new ID.",
        "description": "Pass `{\"custom_id\": \"my-slug\"}` in the body to set a custom URL slug.\nPass `{\"custom_id\": \"\"}` or an empty body to generate a random nanoid.",
        "operationId": "renew_file_id_handler",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The current file ID",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "Optional custom_id and x-delete-token header",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RenewRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "File ID renewed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RenewResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid custom ID"
          },
          "403": {
            "description": "Invalid delete token"
          },
          "404": {
            "description": "File not found"
          },
          "409": {
            "description": "Custom ID already taken"
          },
          "410": {
            "description": "File has expired"
          }
        }
      }
    },
    "/upload": {
      "post": {
        "tags": [
          "Uploads"
        ],
        "operationId": "upload_handler",
        "requestBody": {
          "description": "File upload with optional metadata fields (host, upload_mode, ttl_hours)",
          "content": {
            "multipart/form-data": {}
          }
        },
        "responses": {
          "200": {
            "description": "File uploaded successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadResponse"
                }
              }
            }
          },
          "413": {
            "description": "File too large"
          },
          "429": {
            "description": "Rate limit exceeded"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AdminFileEntry": {
        "type": "object",
        "required": [
          "id",
          "filename",
          "mime_type",
          "size_bytes",
          "uploaded_at",
          "expires_at",
          "url"
        ],
        "properties": {
          "expires_at": {
            "type": "integer",
            "format": "int64"
          },
          "filename": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "mime_type": {
            "type": "string"
          },
          "size_bytes": {
            "type": "integer",
            "format": "int64"
          },
          "storage_host": {
            "type": [
              "string",
              "null"
            ]
          },
          "uploaded_at": {
            "type": "integer",
            "format": "int64"
          },
          "uploader_ip": {
            "type": [
              "string",
              "null"
            ]
          },
          "uploader_ip_hash": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": "string"
          }
        }
      },
      "AnnouncementPayload": {
        "type": "object",
        "required": [
          "message"
        ],
        "properties": {
          "is_active": {
            "type": "boolean"
          },
          "link_url": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "mode": {
            "type": "string"
          }
        }
      },
      "AnnouncementResponse": {
        "type": "object",
        "required": [
          "id",
          "message",
          "link_url",
          "mode",
          "is_active",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "created_at": {
            "type": "integer",
            "format": "int64"
          },
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "is_active": {
            "type": "boolean"
          },
          "link_url": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "mode": {
            "type": "string"
          },
          "updated_at": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "BanIpPayload": {
        "type": "object",
        "properties": {
          "hash": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pre-computed HMAC hash (from click-to-ban). If provided, used directly."
          },
          "ip": {
            "type": [
              "string",
              "null"
            ],
            "description": "Raw IP address (hashed server-side before storage). Used if `hash` is not provided."
          },
          "reason": {
            "type": "string"
          }
        }
      },
      "BanRecord": {
        "type": "object",
        "description": "A banned IP record.",
        "required": [
          "ip",
          "reason",
          "banned_by",
          "banned_at"
        ],
        "properties": {
          "banned_at": {
            "type": "integer",
            "format": "int64"
          },
          "banned_by": {
            "type": "string"
          },
          "ip": {
            "type": "string"
          },
          "reason": {
            "type": "string"
          }
        }
      },
      "CheckResponse": {
        "type": "object",
        "required": [
          "ok",
          "username"
        ],
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "username": {
            "type": "string"
          }
        }
      },
      "FeedbackRecord": {
        "type": "object",
        "description": "A feedback record from the feedback table.",
        "required": [
          "id",
          "message",
          "created_at"
        ],
        "properties": {
          "created_at": {
            "type": "integer",
            "format": "int64"
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "message": {
            "type": "string"
          },
          "reporter_ip": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "FileInfoResponse": {
        "type": "object",
        "description": "Public metadata returned by the file-info and owned-files endpoints.",
        "required": [
          "id",
          "filename",
          "mime_type",
          "size_bytes",
          "uploaded_at",
          "expires_at",
          "url"
        ],
        "properties": {
          "expires_at": {
            "type": "integer",
            "format": "int64"
          },
          "filename": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "mime_type": {
            "type": "string"
          },
          "size_bytes": {
            "type": "integer",
            "format": "int64"
          },
          "storage_host": {
            "type": [
              "string",
              "null"
            ]
          },
          "uploaded_at": {
            "type": "integer",
            "format": "int64"
          },
          "url": {
            "type": "string"
          }
        }
      },
      "FilePair": {
        "type": "object",
        "description": "An ID and delete-token pair to prove you own a file.",
        "required": [
          "id",
          "token"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "token": {
            "type": "string"
          }
        }
      },
      "LoginForm": {
        "type": "object",
        "required": [
          "username",
          "password"
        ],
        "properties": {
          "password": {
            "type": "string"
          },
          "username": {
            "type": "string"
          }
        }
      },
      "LoginResponse": {
        "type": "object",
        "required": [
          "username"
        ],
        "properties": {
          "username": {
            "type": "string"
          }
        }
      },
      "OwnedFilesRequest": {
        "type": "object",
        "description": "Request body for the owned-files bulk lookup endpoint.",
        "required": [
          "pairs"
        ],
        "properties": {
          "pairs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FilePair"
            }
          }
        }
      },
      "OwnedFilesResponse": {
        "type": "object",
        "description": "Response containing the caller's owned files.",
        "required": [
          "files"
        ],
        "properties": {
          "files": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FileInfoResponse"
            }
          }
        }
      },
      "RegisterRequest": {
        "type": "object",
        "required": [
          "id",
          "filename",
          "mime_type",
          "size_bytes"
        ],
        "properties": {
          "filename": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "mime_type": {
            "type": "string"
          },
          "size_bytes": {
            "type": "integer",
            "format": "int64"
          },
          "storage_host": {
            "type": [
              "string",
              "null"
            ]
          },
          "ttl_hours": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "uploader_ip": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "RegisterResponse": {
        "type": "object",
        "description": "Response returned by the register endpoint.",
        "required": [
          "delete_token",
          "expires_at"
        ],
        "properties": {
          "delete_token": {
            "type": "string"
          },
          "expires_at": {
            "type": "integer",
            "format": "int64"
          }
        }
      },
      "RenewRequest": {
        "type": "object",
        "description": "Request body for file-ID renewal. Pass `custom_id` to set a custom URL slug.",
        "properties": {
          "custom_id": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "RenewResponse": {
        "type": "object",
        "description": "Response returned after a successful file-ID renewal.",
        "required": [
          "id",
          "url",
          "filename",
          "expires_at",
          "custom"
        ],
        "properties": {
          "custom": {
            "type": "boolean"
          },
          "expires_at": {
            "type": "integer",
            "format": "int64"
          },
          "filename": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          }
        }
      },
      "ReportRecord": {
        "type": "object",
        "description": "A report record from the reports table.",
        "required": [
          "id",
          "file_url",
          "reason",
          "details",
          "created_at"
        ],
        "properties": {
          "created_at": {
            "type": "integer",
            "format": "int64"
          },
          "details": {
            "type": "string"
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "file_url": {
            "type": "string"
          },
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "reason": {
            "type": "string"
          },
          "reporter_ip": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "UploadResponse": {
        "type": "object",
        "description": "JSON response returned by a successful upload.",
        "required": [
          "id",
          "url",
          "filename",
          "size_bytes",
          "mime_type",
          "expires_at",
          "delete_token",
          "status"
        ],
        "properties": {
          "delete_token": {
            "type": "string"
          },
          "expires_at": {
            "type": "integer",
            "format": "int64"
          },
          "filename": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "mime_type": {
            "type": "string"
          },
          "size_bytes": {
            "type": "integer",
            "format": "int64"
          },
          "status": {
            "type": "string"
          },
          "url": {
            "type": "string"
          }
        }
      }
    },
    "securitySchemes": {
      "api_key": {
        "type": "http",
        "scheme": "bearer"
      },
      "cookie_auth": {
        "type": "http",
        "scheme": "bearer"
      }
    }
  },
  "tags": [
    {
      "name": "Uploads",
      "description": "File upload endpoints"
    },
    {
      "name": "Files",
      "description": "File management endpoints"
    },
    {
      "name": "Admin",
      "description": "Admin panel endpoints. All admin endpoints require a valid JWT token in a cookie named `token`, obtained via POST /api/admin/login."
    },
    {
      "name": "General",
      "description": "General endpoints"
    }
  ]
}