{
  "openapi": "3.1.0",
  "info": {
    "title": "Cloud Storage API",
    "version": "1.0.0",
    "summary": "Programmatic upload, import, and read access.",
    "description": "Authenticate with a Bearer API key (Pro accounts only). Mint and manage keys in the web app under your profile -> API Keys. Every endpoint returns JSON with a top-level `status` of `ok` or `error`; errors also carry a stable machine-readable `code`. Per-key rate limit is 60 requests/minute and every response includes `X-RateLimit-*` headers.",
    "contact": { "name": "API reference", "url": "https://cloudstorage.bar/docs/api" }
  },
  "servers": [ { "url": "https://cloudstorage.bar", "description": "Production" } ],
  "security": [ { "bearerAuth": [] } ],
  "tags": [
    { "name": "Account", "description": "Inspect the calling key." },
    { "name": "Upload", "description": "Upload a file by streaming its bytes." },
    { "name": "Import", "description": "Import from a URL (direct file or video site); asynchronous." },
    { "name": "Files", "description": "Read your folders and files." },
    { "name": "Keys", "description": "Manage API keys (web session + CSRF, not Bearer)." }
  ],
  "paths": {
    "/api/v1/me": {
      "get": {
        "tags": ["Account"], "summary": "Introspect the calling key",
        "description": "Returns the key owner, the key's scopes and expiry, the live rate-limit window, and account limits. Requires only a valid key (no scope).",
        "responses": {
          "200": {
            "description": "Key info.",
            "headers": { "$ref": "#/components/headers/RateLimitSet" },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Me" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/upload": {
      "post": {
        "tags": ["Upload"], "summary": "Upload a file", "x-scope": "upload",
        "description": "Streams the raw request body straight into storage. Send the file bytes as the body and the name as the `filename` query parameter. Max size is the account upload limit (see `/api/v1/me`).",
        "parameters": [
          { "name": "filename", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Destination file name." },
          { "name": "path", "in": "query", "required": false, "schema": { "type": "string", "default": "/" }, "description": "Destination folder (created implicitly)." }
        ],
        "requestBody": {
          "required": true,
          "content": { "application/octet-stream": { "schema": { "type": "string", "format": "binary" } } }
        },
        "responses": {
          "200": {
            "description": "Stored.",
            "headers": { "$ref": "#/components/headers/RateLimitSet" },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UploadResult" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "413": { "$ref": "#/components/responses/TooLarge" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "503": { "$ref": "#/components/responses/Unavailable" }
        }
      }
    },
    "/api/v1/import": {
      "post": {
        "tags": ["Import"], "summary": "Import from a URL", "x-scope": "import",
        "description": "Starts an asynchronous import from `url`. `type:auto` (default) probes the URL: a direct file is fetched as-is, a supported video page is handled by the video engine. Returns a `task_id` to poll. Video imports ignore `path` (they use a per-title folder).",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImportRequest" } } }
        },
        "responses": {
          "200": {
            "description": "Import started.",
            "headers": { "$ref": "#/components/headers/RateLimitSet" },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImportStarted" } } }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/import/status": {
      "get": {
        "tags": ["Import"], "summary": "Poll an import", "x-scope": "import",
        "parameters": [ { "name": "task_id", "in": "query", "required": true, "schema": { "type": "string" } } ],
        "responses": {
          "200": {
            "description": "Task state.",
            "headers": { "$ref": "#/components/headers/RateLimitSet" },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImportStatus" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/import/cancel": {
      "post": {
        "tags": ["Import"], "summary": "Cancel an import", "x-scope": "import",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "type": "object", "required": ["task_id"], "properties": { "task_id": { "type": "string" } } } } }
        },
        "responses": {
          "200": { "description": "Cancelled.", "headers": { "$ref": "#/components/headers/RateLimitSet" }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Ok" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/files": {
      "get": {
        "tags": ["Files"], "summary": "List a folder", "x-scope": "read",
        "description": "Lists files and immediate subfolders at `path`, 50 files per page. Page with `offset`; `has_more` is true when another page may exist. Subfolders are only returned on the first page (`offset=0`).",
        "parameters": [
          { "name": "path", "in": "query", "required": false, "schema": { "type": "string", "default": "/" } },
          { "name": "offset", "in": "query", "required": false, "schema": { "type": "integer", "default": 0, "minimum": 0 } }
        ],
        "responses": {
          "200": { "description": "Listing.", "headers": { "$ref": "#/components/headers/RateLimitSet" }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FileListing" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/folders": {
      "get": {
        "tags": ["Files"], "summary": "Full folder tree", "x-scope": "read",
        "description": "Returns the complete nested folder tree (structure only). Use `/api/v1/files` to read a folder's contents.",
        "responses": {
          "200": { "description": "Tree.", "headers": { "$ref": "#/components/headers/RateLimitSet" }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/FolderTree" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/keys": {
      "get": {
        "tags": ["Keys"], "summary": "List your keys", "security": [ { "cookieAuth": [] } ],
        "description": "Web-session endpoint (cookie + CSRF), used by the app's API Keys panel. Returns key metadata; never the secret.",
        "responses": { "200": { "description": "Keys.", "content": { "application/json": { "schema": { "type": "object" } } } } }
      },
      "post": {
        "tags": ["Keys"], "summary": "Create a key", "security": [ { "cookieAuth": [] } ],
        "description": "Pro only. The full key is returned exactly once. Optional `scopes` (subset of upload/import/read; default all) and `expires_in_days`.",
        "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateKeyRequest" } } } },
        "responses": { "200": { "description": "Created (key shown once).", "content": { "application/json": { "schema": { "type": "object" } } } } }
      }
    },
    "/api/keys/{id}/revoke": {
      "post": {
        "tags": ["Keys"], "summary": "Revoke a key", "security": [ { "cookieAuth": [] } ],
        "parameters": [ { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } } ],
        "responses": { "200": { "description": "Revoked.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Ok" } } } } }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": { "type": "http", "scheme": "bearer", "description": "Your API key sent as `Authorization: Bearer csb_live_...`." },
      "cookieAuth": { "type": "apiKey", "in": "cookie", "name": "session", "description": "Web session cookie + CSRF; used by the app UI, not for programmatic access." }
    },
    "headers": {
      "RateLimitSet": {
        "X-RateLimit-Limit": { "schema": { "type": "integer" }, "description": "Requests allowed per 60s window." },
        "X-RateLimit-Remaining": { "schema": { "type": "integer" }, "description": "Requests left in the current window." },
        "X-RateLimit-Reset": { "schema": { "type": "integer" }, "description": "Unix epoch second when a slot frees." }
      }
    },
    "responses": {
      "BadRequest": { "description": "Invalid request.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Unauthorized": { "description": "Missing, invalid, revoked, or expired key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Forbidden": { "description": "Not a Pro account, banned, or the key lacks the required scope.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "NotFound": { "description": "Resource not found.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "TooLarge": { "description": "Body exceeds the upload limit.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "RateLimited": {
        "description": "Per-key rate limit exceeded.",
        "headers": { "Retry-After": { "schema": { "type": "integer" }, "description": "Seconds to wait before retrying." } },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Unavailable": { "description": "API disabled or storage temporarily unavailable.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    },
    "schemas": {
      "Ok": { "type": "object", "properties": { "status": { "const": "ok" } } },
      "Error": {
        "type": "object", "required": ["status", "code", "message"],
        "properties": {
          "status": { "const": "error" },
          "code": { "type": "string", "enum": ["feature_disabled", "missing_auth", "invalid_key", "expired_key", "account_unavailable", "premium_required", "rate_limited", "insufficient_scope", "invalid_request", "not_found", "payload_too_large", "unavailable", "server_error"] },
          "message": { "type": "string" }
        }
      },
      "Scope": { "type": "string", "enum": ["upload", "import", "read"] },
      "Me": {
        "type": "object",
        "properties": {
          "status": { "const": "ok" },
          "username": { "type": "string" },
          "plan": { "type": "string" },
          "key": {
            "type": "object",
            "properties": {
              "id": { "type": "string" }, "name": { "type": "string" },
              "scopes": { "type": "array", "items": { "$ref": "#/components/schemas/Scope" } },
              "key_prefix": { "type": "string" }, "key_last4": { "type": "string" },
              "created_at": { "type": "string", "format": "date-time" },
              "expires_at": { "type": ["string", "null"], "format": "date-time" }
            }
          },
          "rate_limit": { "type": "object", "properties": { "limit": { "type": "integer" }, "remaining": { "type": "integer" }, "reset": { "type": "integer" }, "window_seconds": { "type": "integer" } } },
          "limits": { "type": "object", "properties": { "upload_max_bytes": { "type": "integer" } } }
        }
      },
      "UploadResult": {
        "type": "object",
        "properties": {
          "status": { "const": "ok" }, "tracking_id": { "type": "string" },
          "file_name": { "type": "string" }, "size": { "type": "integer" },
          "link": { "type": "string", "format": "uri" }, "folder": { "type": "string" }
        }
      },
      "ImportRequest": {
        "type": "object", "required": ["url"],
        "properties": {
          "url": { "type": "string", "format": "uri" },
          "type": { "type": "string", "enum": ["auto", "file", "video"], "default": "auto" },
          "path": { "type": "string", "default": "/", "description": "Destination folder for file imports." },
          "filename": { "type": "string", "description": "Optional name override for file imports." },
          "video": {
            "type": "object",
            "properties": {
              "quality": { "type": "string", "description": "best | audio | a max height like 1080", "default": "best" },
              "subtitles": { "type": "boolean" }, "thumbnail": { "type": "boolean" }
            }
          }
        }
      },
      "ImportStarted": {
        "type": "object",
        "properties": { "status": { "const": "ok" }, "type": { "type": "string", "enum": ["file", "video"] }, "task_id": { "type": "string", "description": "Prefixed: file_<id> or video_<id>." } }
      },
      "ImportStatus": {
        "type": "object",
        "properties": {
          "status": { "const": "ok" },
          "task": {
            "type": "object",
            "properties": {
              "id": { "type": "string" }, "type": { "type": "string", "enum": ["file", "video"] },
              "state": { "type": "string", "enum": ["running", "completed", "failed", "cancelled"] },
              "progress": { "type": "string" }, "link": { "type": "string" },
              "folder": { "type": "string" }, "error": { "type": "string" }
            }
          }
        }
      },
      "FileObject": {
        "type": "object",
        "properties": { "name": { "type": "string" }, "size": { "type": "integer" }, "link": { "type": "string", "format": "uri" }, "uploaded_at": { "type": ["string", "null"], "format": "date-time" } }
      },
      "FileListing": {
        "type": "object",
        "properties": {
          "status": { "const": "ok" }, "path": { "type": "string" },
          "folders": { "type": "array", "items": { "type": "string" } },
          "files": { "type": "array", "items": { "$ref": "#/components/schemas/FileObject" } },
          "limit": { "type": "integer" }, "next_offset": { "type": "integer" }, "has_more": { "type": "boolean" }
        }
      },
      "FolderNode": {
        "type": "object",
        "properties": { "name": { "type": "string" }, "path": { "type": "string" }, "folders": { "type": "array", "items": { "$ref": "#/components/schemas/FolderNode" } } }
      },
      "FolderTree": { "type": "object", "properties": { "status": { "const": "ok" }, "tree": { "$ref": "#/components/schemas/FolderNode" } } },
      "CreateKeyRequest": {
        "type": "object",
        "properties": {
          "name": { "type": "string", "maxLength": 64 },
          "scopes": { "type": "array", "items": { "$ref": "#/components/schemas/Scope" } },
          "expires_in_days": { "type": ["integer", "null"], "minimum": 1, "maximum": 3650 }
        }
      }
    }
  }
}
