{"openapi":"3.0.3","info":{"title":"CDN API","description":"API documentation generated from Fastify routes and schemas. Upload endpoints accept multipart form data up to 100 MB per file.","version":"1.0.0"},"components":{"schemas":{},"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"Dashboard API key. Send as `Authorization: Bearer <key>` or pass the raw key in the Authorization header."}}},"paths":{"/v1/file":{"post":{"summary":"Upload file","tags":["Uploads"],"parameters":[{"schema":{"type":"string"},"in":"header","name":"authorization","required":true,"description":"API key or Bearer API key. Example: Bearer qbox_live_abc123"}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Storage object ID (full storage key)"},"url":{"type":"string","format":"uri"}},"required":["id","url"]},"status":{"type":"string","enum":["ok"]}},"required":["data","status"]},"examples":{"success":{"value":{"data":{"id":"acme-org/images/example.png","url":"https://r2.qbox.re/acme-org/images/example.png"},"status":"ok"}}}}}},"400":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"missingFile":{"value":{"error":"No file uploaded","code":"MISSING_FILE"}},"invalidFilename":{"value":{"error":"Invalid filename","code":"INVALID_FILENAME"}},"invalidPath":{"value":{"error":"Invalid path","code":"INVALID_PATH"}}}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"missingApiKey":{"value":{"error":"Missing API key","code":"MISSING_API_KEY"}}}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"mediaTypeNotAllowed":{"value":{"error":"API key is not allowed to upload media type image/webp","code":"MEDIA_TYPE_NOT_ALLOWED"}},"invalidMetadata":{"value":{"error":"API key metadata is missing organization context","code":"INVALID_KEY_METADATA"}}}}}},"413":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string"},"usageBytes":{"type":"integer","minimum":0},"uploadBytes":{"type":"integer","minimum":0},"limitBytes":{"type":"integer","minimum":0},"remainingBytes":{"type":"integer","minimum":0},"plan":{"type":"string"}},"required":["error","code"]},"examples":{"storageLimit":{"value":{"error":"Organization storage limit exceeded","code":"ORG_STORAGE_LIMIT_EXCEEDED","usageBytes":2147483648,"uploadBytes":7340032,"limitBytes":2147483648,"remainingBytes":0,"plan":"free"}},"fileTooLarge":{"value":{"error":"Uploaded file exceeds maximum size of 100 MB","code":"FILE_TOO_LARGE"}}}}}},"429":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string"},"tryAgainIn":{"type":"integer","minimum":1}},"required":["error","code"]},"examples":{"rateLimit":{"value":{"error":"Too many requests","code":"RATE_LIMITED","tryAgainIn":1000}}}}}},"500":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"storageNotConfigured":{"value":{"error":"R2 storage is not configured","code":"STORAGE_NOT_CONFIGURED"}}}}}},"503":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"storageLimitCheckFailed":{"value":{"error":"Unable to verify organization storage limit","code":"STORAGE_LIMIT_CHECK_FAILED"}},"planLimitNotConfigured":{"value":{"error":"Organization plan storage limit is not configured","code":"PLAN_STORAGE_LIMIT_NOT_CONFIGURED"}}}}}}},"requestBody":{"required":true,"description":"Multipart form upload. Only `file` is required; `filename` and `path` are optional.","content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"File to upload (required). Maximum size: 100 MB."},"filename":{"type":"string","description":"Optional. Custom filename (example: logo.png). If omitted, a random name is generated."},"path":{"type":"string","description":"Optional. Storage folder path within the API key folder (example: super)."}},"required":["file"]}}}},"security":[{"BearerAuth":[]}]}},"/v1/file/presigned-url":{"get":{"summary":"Create Presigned URL","tags":["Uploads"],"parameters":[{"schema":{"type":"string"},"in":"header","name":"authorization","required":true,"description":"API key or Bearer API key. Example: Bearer qbox_live_abc123"}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"presignedUrl":{"type":"string","format":"uri"},"expiresIn":{"type":"integer","minimum":1}},"required":["presignedUrl","expiresIn"]},"status":{"type":"string","enum":["ok"]}},"required":["data","status"]},"examples":{"success":{"value":{"data":{"presignedUrl":"https://api.qbox.re/v1/file/presigned-url/eyJ2IjoxLCJleHAiOjE3NDAwMDAwMDAsIm9yZ0lkIjoiYWJjIn0.signature","expiresIn":300},"status":"ok"}}}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"missingApiKey":{"value":{"error":"Missing API key","code":"MISSING_API_KEY"}}}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"invalidMetadata":{"value":{"error":"API key metadata is missing organization context","code":"INVALID_KEY_METADATA"}}}}}},"429":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string"},"tryAgainIn":{"type":"integer","minimum":1}},"required":["error","code"]},"examples":{"rateLimit":{"value":{"error":"Too many requests","code":"RATE_LIMITED","tryAgainIn":1000}}}}}},"500":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"missingPresignedSecret":{"value":{"error":"Presigned URL secret is not configured","code":"PRESIGNED_URL_SECRET_NOT_CONFIGURED"}}}}}}},"security":[{"BearerAuth":[]}]}},"/v1/file/presigned-url/{token}":{"post":{"summary":"Upload file (Presigned URL)","tags":["Uploads"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"token","required":true,"description":"URL-encoded presigned upload token. Example: eyJ2IjoxLCJleHAiOjE3NDAwMDAwMDAsIm9yZ0lkIjoiYWJjIn0.signature"}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Storage object ID (full storage key)"},"url":{"type":"string","format":"uri"}},"required":["id","url"]},"status":{"type":"string","enum":["ok"]}},"required":["data","status"]},"examples":{"success":{"value":{"data":{"id":"acme-org/images/example.png","url":"https://r2.qbox.re/acme-org/images/example.png"},"status":"ok"}}}}}},"400":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"missingFile":{"value":{"error":"No file uploaded","code":"MISSING_FILE"}},"malformedToken":{"value":{"error":"Malformed presigned upload token","code":"MALFORMED_PRESIGNED_UPLOAD_TOKEN"}},"invalidFilename":{"value":{"error":"Invalid filename","code":"INVALID_FILENAME"}},"invalidPath":{"value":{"error":"Invalid path","code":"INVALID_PATH"}}}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"invalidToken":{"value":{"error":"Invalid or expired presigned upload token","code":"INVALID_PRESIGNED_UPLOAD_TOKEN"}}}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"mediaTypeNotAllowed":{"value":{"error":"Presigned upload is not allowed for media type image/webp","code":"MEDIA_TYPE_NOT_ALLOWED"}}}}}},"413":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string"},"usageBytes":{"type":"integer","minimum":0},"uploadBytes":{"type":"integer","minimum":0},"limitBytes":{"type":"integer","minimum":0},"remainingBytes":{"type":"integer","minimum":0},"plan":{"type":"string"}},"required":["error","code"]},"examples":{"storageLimit":{"value":{"error":"Organization storage limit exceeded","code":"ORG_STORAGE_LIMIT_EXCEEDED","usageBytes":2147483648,"uploadBytes":7340032,"limitBytes":2147483648,"remainingBytes":0,"plan":"free"}},"fileTooLarge":{"value":{"error":"Uploaded file exceeds maximum size of 100 MB","code":"FILE_TOO_LARGE"}}}}}},"500":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"storageNotConfigured":{"value":{"error":"R2 storage is not configured","code":"STORAGE_NOT_CONFIGURED"}}}}}},"503":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"storageLimitCheckFailed":{"value":{"error":"Unable to verify organization storage limit","code":"STORAGE_LIMIT_CHECK_FAILED"}},"planLimitNotConfigured":{"value":{"error":"Organization plan storage limit is not configured","code":"PLAN_STORAGE_LIMIT_NOT_CONFIGURED"}}}}}}},"requestBody":{"required":true,"description":"Multipart form upload. Only `file` is required; `filename` and `path` are optional.","content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"File to upload (required). Maximum size: 100 MB."},"filename":{"type":"string","description":"Optional. Custom filename (example: logo.png). If omitted, a random name is generated."},"path":{"type":"string","description":"Optional. Storage folder path within the API key folder (example: super)."}},"required":["file"]}}}}}},"/v1/file/{path}":{"delete":{"summary":"Delete file","tags":["Uploads"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"path","required":true,"description":"URL-encoded storage key path. Example: acme-org%2Fimages%2Fexample.png"},{"schema":{"type":"string"},"in":"header","name":"authorization","required":true,"description":"API key or Bearer API key. Example: Bearer qbox_live_abc123"}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["ok"]}},"required":["status"]},"examples":{"success":{"value":{"status":"ok"}}}}}},"400":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"missingPath":{"value":{"error":"Missing file path","code":"MISSING_FILE_PATH"}},"malformedPath":{"value":{"error":"Malformed file path","code":"MALFORMED_FILE_PATH"}},"invalidPath":{"value":{"error":"Invalid file path","code":"INVALID_FILE_PATH"}}}}}},"401":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"missingApiKey":{"value":{"error":"Missing API key","code":"MISSING_API_KEY"}}}}}},"403":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"deleteNotAllowed":{"value":{"error":"API key is not allowed to delete media","code":"DELETE_NOT_ALLOWED"}},"orgScopeViolation":{"value":{"error":"File does not belong to API key organization","code":"ORG_SCOPE_VIOLATION"}},"mediaTypeNotAllowed":{"value":{"error":"API key is not allowed to delete media type image/png","code":"MEDIA_TYPE_NOT_ALLOWED"}}}}}},"404":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"fileNotFound":{"value":{"error":"File not found","code":"FILE_NOT_FOUND"}}}}}},"429":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string"},"tryAgainIn":{"type":"integer","minimum":1}},"required":["error","code"]},"examples":{"rateLimit":{"value":{"error":"Too many requests","code":"RATE_LIMITED","tryAgainIn":1000}}}}}},"500":{"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message. Example: Missing API key"},"code":{"type":"string","description":"Machine-readable error code. Example: MISSING_API_KEY"}},"required":["error","code"]},"examples":{"storageNotConfigured":{"value":{"error":"R2 storage is not configured","code":"STORAGE_NOT_CONFIGURED"}}}}}}},"security":[{"BearerAuth":[]}]}}},"servers":[{"url":"https://api.qbox.re"}]}