{"openapi":"3.0.0","info":{"title":"API Documentation","version":"1.0.0","description":"# API Documentation","contact":{"name":"API Support","email":"support@jsoncut.com"},"license":{"name":"MIT","url":"https://opensource.org/licenses/MIT"}},"servers":[{"url":"https://api.jsoncut.com","description":"Main server"}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"API key for programmatic access"}},"schemas":{"ApiResponse":{"type":"object","properties":{"success":{"type":"boolean","description":"Indicates if the request was successful"},"data":{"description":"Response data (only when success is true)"},"message":{"type":"string","description":"Optional success message (only when success is true)"}},"required":["success"]},"ValidationApiResponse":{"type":"object","properties":{"success":{"type":"boolean","description":"Indicates if the request was successful"},"data":{"description":"Response data (only when success is true)"}},"required":["success"]},"PaginatedResponse":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"pagination":{"type":"object","properties":{"page":{"type":"integer","minimum":1,"description":"Current page number"},"limit":{"type":"integer","minimum":1,"maximum":100,"description":"Number of items per page"},"total":{"type":"integer","minimum":0,"description":"Total number of items"},"totalPages":{"type":"integer","minimum":0,"description":"Total number of pages"}},"required":["page","limit","total","totalPages"]}}}]},"JobPaginatedResponse":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"pagination":{"type":"object","properties":{"page":{"type":"integer","minimum":1,"description":"Current page number"},"limit":{"type":"integer","minimum":1,"maximum":100,"description":"Number of items per page"},"totalCount":{"type":"integer","minimum":0,"description":"Total number of jobs"},"totalPages":{"type":"integer","minimum":0,"description":"Total number of pages"},"hasNextPage":{"type":"boolean","description":"Whether there are more pages"},"hasPrevPage":{"type":"boolean","description":"Whether there are previous pages"}},"required":["page","limit","totalCount","totalPages","hasNextPage","hasPrevPage"]}}}]},"UploadFileResponse":{"type":"object","properties":{"id":{"type":"string","format":"cuid","description":"Unique identifier for the uploaded file"},"filename":{"type":"string","description":"Generated filename on the server"},"originalName":{"type":"string","description":"Original filename as uploaded by user"},"mimeType":{"type":"string","description":"MIME type of the file"},"storageUrl":{"type":"string","description":"Relative path to use in job configurations"},"metadata":{"type":"object","description":"File metadata including validation results","properties":{"category":{"type":"string","enum":["image","video","audio","font"]},"validationResult":{"type":"object","properties":{"errors":{"type":"array","items":{"type":"string"}},"isValid":{"type":"boolean"},"fileSize":{"type":"integer"},"detectedMimeType":{"type":"string"}}}}},"expiresAt":{"type":"string","format":"date-time","description":"Timestamp when the file will be automatically deleted"},"createdAt":{"type":"string","format":"date-time","description":"Timestamp when the file was uploaded"}},"required":["id","filename","originalName","mimeType","storageUrl","metadata","createdAt"]},"FileResponse":{"type":"object","properties":{"id":{"type":"string","format":"cuid","description":"Unique identifier for the file"},"filename":{"type":"string","description":"Generated filename on the server"},"originalName":{"type":"string","description":"Original filename as uploaded by user"},"mimeType":{"type":"string","description":"MIME type of the file"},"width":{"type":"integer","nullable":true,"description":"Width in pixels (for images/videos)"},"height":{"type":"integer","nullable":true,"description":"Height in pixels (for images/videos)"},"duration":{"type":"number","nullable":true,"description":"Duration in seconds (for videos/audio)"},"storageUrl":{"type":"string","description":"Relative path to use in job configurations"},"metadata":{"type":"object","description":"File metadata including validation results","properties":{"category":{"type":"string","enum":["image","video","audio","font","unknown"]},"validationResult":{"type":"object","properties":{"errors":{"type":"array","items":{"type":"string"}},"isValid":{"type":"boolean"},"fileSize":{"type":"integer"},"detectedMimeType":{"type":"string"}}}}},"expiresAt":{"type":"string","format":"date-time","description":"Timestamp when the file will be automatically deleted"},"createdAt":{"type":"string","format":"date-time","description":"Timestamp when the file was uploaded"},"updatedAt":{"type":"string","format":"date-time","description":"Timestamp when the file was last updated"}},"required":["id","filename","originalName","mimeType","storageUrl","metadata","createdAt","updatedAt"]},"FileMetadataResponse":{"type":"object","properties":{"storageUrl":{"type":"string","description":"Relative path to use in job configurations"},"metadata":{"type":"object","description":"Additional file metadata including validation results"},"updatedAt":{"type":"string","format":"date-time","description":"Timestamp when the file was last updated"}},"required":["storageUrl","metadata","updatedAt"]},"CreateJobRequest":{"type":"object","required":["type","config"],"properties":{"type":{"type":"string","enum":["image","video"],"description":"Type of job to create"},"config":{"type":"object","description":"Job configuration (varies by type)"}},"example":{"type":"image","config":{"width":800,"height":600,"layers":[{"type":"image","path":"/input/user123/background.jpg","position":"center"},{"type":"text","text":"Hello World","position":"center","fontSize":48,"color":"#ffffff"}]}}},"ValidateJobRequest":{"type":"object","required":["type","config"],"properties":{"type":{"type":"string","enum":["image","video"],"description":"Type of job to validate"},"config":{"type":"object","description":"Job configuration to validate"}},"example":{"type":"video","config":{"width":1920,"height":1080,"fps":30,"clips":[{"duration":5,"layers":[{"type":"video","path":"/input/user123/video.mp4","resizeMode":"cover"},{"type":"title","text":"My Video","position":"center","fontSize":72,"color":"#ffffff"}]}]}}},"JobResponse":{"type":"object","properties":{"id":{"type":"string","description":"Job ID"},"type":{"type":"string","enum":["IMAGE","VIDEO"],"description":"Job type"},"status":{"type":"string","enum":["PENDING","PROCESSING","COMPLETED","FAILED","CANCELLED"],"description":"Current job status"},"config":{"type":"object","description":"Job configuration"},"inputFileIds":{"type":"array","items":{"type":"string"},"description":"Array of input MediaFile IDs (use these for downloads)"},"outputFormat":{"type":"string","description":"Output format (png, mp4, etc.)"},"outputFileId":{"type":"string","nullable":true,"description":"Output MediaFile ID (use this for download when job is completed)"},"tokensUsed":{"type":"integer","description":"Tokens consumed by this job"},"errorMessage":{"type":"string","nullable":true,"description":"Error message if job failed"},"startedAt":{"type":"string","format":"date-time","nullable":true,"description":"When job processing started"},"completedAt":{"type":"string","format":"date-time","nullable":true,"description":"When job processing completed"},"createdAt":{"type":"string","format":"date-time","description":"When job was created"},"updatedAt":{"type":"string","format":"date-time","description":"When job was last updated"},"priority":{"type":"integer","description":"Job priority (0-10)"}}},"JobListResponse":{"type":"object","properties":{"id":{"type":"string","description":"Job ID"},"type":{"type":"string","enum":["IMAGE","VIDEO"],"description":"Job type"},"status":{"type":"string","enum":["PENDING","PROCESSING","COMPLETED","FAILED","CANCELLED"],"description":"Current job status"},"outputFormat":{"type":"string","description":"Output format (png, mp4, etc.)"},"outputFileId":{"type":"string","nullable":true,"description":"Output MediaFile ID (use this for download when job is completed)"},"tokensUsed":{"type":"integer","description":"Tokens consumed by this job"},"errorMessage":{"type":"string","nullable":true,"description":"Error message if job failed"},"startedAt":{"type":"string","format":"date-time","nullable":true,"description":"When job processing started"},"completedAt":{"type":"string","format":"date-time","nullable":true,"description":"When job processing completed"},"createdAt":{"type":"string","format":"date-time","description":"When job was created"},"updatedAt":{"type":"string","format":"date-time","description":"When job was last updated"}}},"JobCreateResponse":{"type":"object","properties":{"jobId":{"type":"string","description":"Unique identifier for the created job"},"status":{"type":"string","enum":["PENDING","PROCESSING","COMPLETED","FAILED","CANCELLED"],"description":"Initial job status (always PENDING)"},"type":{"type":"string","enum":["IMAGE","VIDEO"],"description":"Type of job created"},"estimatedTokens":{"type":"integer","description":"Estimated token cost (final cost calculated upon completion)"},"createdAt":{"type":"string","format":"date-time","description":"Timestamp when the job was created"}},"required":["jobId","status","type","estimatedTokens","createdAt"]},"ValidationResponse":{"type":"object","properties":{"validation":{"type":"object","properties":{"isValid":{"type":"boolean","description":"Whether the file validation passed"},"fileSize":{"type":"integer","description":"File size in bytes"},"fileType":{"type":"string","description":"Detected file extension"},"detectedMimeType":{"type":"string","description":"Detected MIME type"},"errors":{"type":"array","items":{"type":"string"},"description":"Validation error messages"},"category":{"type":"string","enum":["image","video","audio","font","unknown"],"description":"Detected file category"}},"required":["isValid","fileSize","fileType","errors","category"]},"uploadedFile":{"type":"object","properties":{"originalName":{"type":"string","description":"Original filename"},"mimetype":{"type":"string","description":"Original MIME type"},"size":{"type":"integer","description":"File size in bytes"}},"required":["originalName","mimetype","size"]}},"required":["validation","uploadedFile"]},"Error":{"type":"object","properties":{"success":{"type":"boolean","example":false,"description":"Always false for errors"},"error":{"type":"string","description":"Error message"}},"required":["success","error"]},"WebhookJobPayload":{"type":"object","required":["jobId","status","type","timestamp"],"properties":{"jobId":{"type":"string","description":"Unique job identifier"},"status":{"type":"string","enum":["processing","completed","failed","cancelled"],"description":"Current job status"},"type":{"type":"string","enum":["image","video"],"description":"Type of job"},"timestamp":{"type":"string","format":"date-time","description":"Timestamp of status change"},"resultUrl":{"type":"string","format":"uri","description":"URL to the generated media file (for completed jobs)"},"error":{"type":"string","description":"Error message (for failed jobs)"},"metadata":{"type":"object","properties":{"processingTime":{"type":"number","description":"Processing time in milliseconds"},"fileSize":{"type":"number","description":"Output file size in bytes"},"sessionId":{"type":"string","description":"Runner session identifier"},"videoDuration":{"type":"number","description":"Video duration in seconds (for final token calculation)"},"outputWidth":{"type":"number","description":"Output width in pixels"},"outputHeight":{"type":"number","description":"Output height in pixels"}}}},"example":{"jobId":"job_123456","status":"completed","type":"video","timestamp":"2024-01-01T12:00:00Z","resultUrl":"http://localhost:9000/media-output/video/2024-01-01/job_123456/output.mp4","metadata":{"processingTime":45000,"fileSize":1048576,"videoDuration":30.5,"outputWidth":1920,"outputHeight":1080}}},"BillingPlan":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"displayName":{"type":"string"},"description":{"type":"string","nullable":true},"tokensPerMonth":{"type":"integer","minimum":0},"pricePerMonth":{"type":"number","nullable":true},"maxConcurrentJobs":{"type":"integer","minimum":1},"supportLevel":{"type":"string","enum":["community","email","priority"]}}},"TokenUsageInfo":{"type":"object","properties":{"monthlyUsed":{"type":"integer","minimum":0},"monthlyQuota":{"type":"integer","minimum":0},"monthlyRemaining":{"type":"integer","minimum":0},"prepaidBalance":{"type":"integer","minimum":0},"totalAvailable":{"type":"integer","minimum":0},"currentPeriodEnd":{"type":"string","format":"date-time"}}},"CurrentPlanResponse":{"type":"object","properties":{"plan":{"$ref":"#/components/schemas/BillingPlan"},"usage":{"$ref":"#/components/schemas/TokenUsageInfo"}}},"RegisterRequest":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email","description":"User's email address"},"name":{"type":"string","description":"User's display name"},"hcaptchaToken":{"type":"string","description":"hCaptcha verification token (required when hCaptcha is enabled)"}},"example":{"email":"user@example.com","name":"John Doe","hcaptchaToken":"P1_eyJ0eXAiOiJKV1..."}},"LoginRequest":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email","description":"User's email address"},"hcaptchaToken":{"type":"string","description":"hCaptcha verification token (required when hCaptcha is enabled)"}},"example":{"email":"user@example.com","hcaptchaToken":"P1_eyJ0eXAiOiJKV1..."}},"VerifyTokenRequest":{"type":"object","required":["token","email"],"properties":{"token":{"type":"string","description":"6-digit numeric login code from email"},"email":{"type":"string","format":"email","description":"User's email address"}},"example":{"token":"123456","email":"user@example.com"}},"UpdateProfileRequest":{"type":"object","properties":{"name":{"type":"string","description":"Updated display name"},"email":{"type":"string","format":"email","description":"Updated email address"}},"example":{"name":"Jane Doe","email":"jane@example.com"}},"CreateApiKeyRequest":{"type":"object","required":["name","permissions"],"properties":{"name":{"type":"string","minLength":3,"maxLength":100,"description":"Human-readable name for the API key"},"expiresAt":{"type":"string","format":"date-time","description":"Optional expiration date (must be in the future).\nIf not provided or null, the API key will never expire.\n"},"permissions":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/Permission"},"description":"List of permissions for this API key. Each permission grants access to specific resources and actions.\n\n**Available Resources:**\n- `media` - File upload, download, and management operations\n- `jobs` - Job creation, monitoring, and management\n\n**Available Actions:**\n- `read` - Read-only access (GET requests only)\n- `full` - Full access (all operations: GET, POST, PUT, DELETE)\n\n**Permission Combinations:**\n- **Read-only access**: `[{resource: \"media\", action: \"read\"}]` - Can only view and download files\n- **Full access**: `[{resource: \"media\", action: \"full\"}, {resource: \"jobs\", action: \"full\"}]` - Complete access to all operations\n"}},"examples":{"readonly":{"summary":"Read-only API key for viewing files","value":{"name":"Read-only Access","permissions":[{"resource":"media","action":"read"}]}},"fullaccess":{"summary":"Full access API key for all operations","value":{"name":"Full Access","expiresAt":"2024-12-31T23:59:59Z","permissions":[{"resource":"media","action":"full"},{"resource":"jobs","action":"full"}]}}}}},"responses":{"BadRequest":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false,"description":"Always false for errors"},"error":{"type":"string","example":"Bad request","description":"Error message"}},"required":["success","error"]}}}},"UnauthorizedError":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false,"description":"Always false for errors"},"error":{"type":"string","example":"Unauthorized","description":"Error message"}},"required":["success","error"]}}}},"Unauthorized":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false,"description":"Always false for errors"},"error":{"type":"string","example":"Unauthorized","description":"Error message"}},"required":["success","error"]}}}},"ForbiddenError":{"description":"Insufficient permissions","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"error":{"example":"Forbidden"}}}]}}}},"NotFoundError":{"description":"Not Found","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"error":{"example":"Resource not found"}}}]}}}},"NotFound":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false,"description":"Always false for errors"},"error":{"type":"string","example":"Resource not found","description":"Error message"}},"required":["success","error"]}}}},"ValidationError":{"description":"Validation failed","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"error":{"example":"Email is required"}}}]}}}},"RateLimitError":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"error":{"example":"Too many requests, please try again later"}}}]}}}},"InternalServerError":{"description":"Internal server error","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false,"description":"Always false for errors"},"error":{"type":"string","example":"Internal server error","description":"Error message"}},"required":["success","error"]}}}}}},"tags":[{"name":"Files","description":"File upload, download, and management"},{"name":"Jobs","description":"Job creation, validation, and monitoring"}],"paths":{"/api/v1/webhooks/runner/job-status":{"post":{"summary":"Receive job status updates from runner","description":"Webhook endpoint for receiving job status updates from the media processing runner.\nThis endpoint updates the job status in the database and handles final token calculation.\n\n**Token Calculation:**\n- **Images:** 2 base tokens + 1 token per layer\n- **Videos:** Duration-based calculation (10 base + 2 per second) or fallback to reserved amount\n- **Failed jobs:** No tokens charged\n\n**Status Flow:**\n1. `processing` - Job started processing\n2. `completed` - Job finished successfully (tokens charged)\n3. `failed` - Job failed (no tokens charged)\n4. `cancelled` - Job was cancelled (no tokens charged)\n","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookJobPayload"}}}},"responses":{"200":{"description":"Webhook processed successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"received":{"type":"boolean","example":true}}}}}]}}}},"400":{"description":"Invalid webhook payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/webhooks/health":{"get":{"summary":"Webhook system health check","description":"Check if the webhook system is operational","tags":["Webhooks"],"responses":{"200":{"description":"Webhook system is healthy","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string","example":"healthy"},"timestamp":{"type":"string","format":"date-time"},"webhookEndpoint":{"type":"string","example":"/api/v1/webhooks/runner/job-status"}}}}}]}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/user-api/docs":{"get":{"summary":"Get end user API documentation","description":"Get the OpenAPI/Swagger documentation for end users.\nThis documentation only includes endpoints that can be accessed with API keys.\n","tags":["Documentation"],"responses":{"200":{"description":"API documentation","content":{"application/json":{"schema":{"type":"object","description":"OpenAPI specification"}}}}}}},"/api/v1/user-api/docs/ui":{"get":{"summary":"Get end user API documentation UI","description":"Get the Swagger UI for end user API documentation.\nThis provides an interactive interface for exploring the API.\n","tags":["Documentation"],"responses":{"200":{"description":"Swagger UI HTML page","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/v1/tools/video/duration":{"post":{"summary":"Get video duration","description":"Calculate and return the duration of a video file. Accepts either a public URL or an internal storage path.","tags":["Tools","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","description":"Internal storage path (starts with /). Use this for files uploaded via the Files API.","example":"/video/2024-01-15/user123/abc123.mp4"},"url":{"type":"string","description":"Public URL of the video file (http/https)","example":"https://example.com/video.mp4"}},"oneOf":[{"required":["path"]},{"required":["url"]}]}}}},"responses":{"200":{"description":"Video duration retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"durationMs":{"type":"number","description":"Duration in milliseconds","example":125500},"durationFormatted":{"type":"string","description":"Duration formatted as HH:MM:SS.mmm or MM:SS.mmm","example":"02:05.500"},"source":{"type":"string","enum":["storage","url"],"description":"Source of the file (internal storage or external URL)"},"originalPath":{"type":"string","description":"The original path or URL provided in the request"}}}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"File not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Unable to process the video file","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/tools/audio/duration":{"post":{"summary":"Get audio duration","description":"Calculate and return the duration of an audio file. Accepts either a public URL or an internal storage path.","tags":["Tools","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","description":"Internal storage path (starts with /). Use this for files uploaded via the Files API.","example":"/audio/2024-01-15/user123/abc123.mp3"},"url":{"type":"string","description":"Public URL of the audio file (http/https)","example":"https://example.com/audio.mp3"}},"oneOf":[{"required":["path"]},{"required":["url"]}]}}}},"responses":{"200":{"description":"Audio duration retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"durationMs":{"type":"number","description":"Duration in milliseconds","example":180250},"durationFormatted":{"type":"string","description":"Duration formatted as HH:MM:SS.mmm or MM:SS.mmm","example":"03:00.250"},"source":{"type":"string","enum":["storage","url"],"description":"Source of the file (internal storage or external URL)"},"originalPath":{"type":"string","description":"The original path or URL provided in the request"}}}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"File not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Unable to process the audio file","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/tools/video/duration/bulk":{"post":{"summary":"Get video duration for multiple files (bulk)","description":"Calculate and return the duration of multiple video files. Accepts an array of public URLs or internal storage paths. Maximum 5 files for free plan, 300 files for paid plans. Costs 1 token per file.","tags":["Tools","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["files"],"properties":{"files":{"type":"array","description":"Array of file paths or URLs. Can be strings or objects with path/url property. Limit: 5 files (free plan) or 300 files (paid plans).","maxItems":300,"items":{"oneOf":[{"type":"string","description":"Internal storage path (starts with /) or public URL (http/https)","example":"/video/2024-01-15/user123/abc123.mp4"},{"type":"object","properties":{"path":{"type":"string","description":"Internal storage path"},"url":{"type":"string","description":"Public URL"}}}]}}}},"examples":{"stringArray":{"summary":"Array of strings","value":{"files":["/video/2024-01-15/user123/video1.mp4","https://example.com/video2.mp4"]}},"objectArray":{"summary":"Array of objects","value":{"files":[{"path":"/video/2024-01-15/user123/video1.mp4"},{"url":"https://example.com/video2.mp4"}]}}}}}},"responses":{"200":{"description":"Bulk video duration calculation completed","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"results":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"durationMs":{"type":"number","description":"Duration in milliseconds"},"durationFormatted":{"type":"string","description":"Duration formatted as HH:MM:SS.mmm or MM:SS.mmm"},"source":{"type":"string","enum":["storage","url"]},"originalPath":{"type":"string"}}},{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"string"},"originalPath":{"type":"string"}}}]}},"summary":{"type":"object","properties":{"total":{"type":"integer","description":"Total number of files processed"},"successful":{"type":"integer","description":"Number of successfully processed files"},"failed":{"type":"integer","description":"Number of failed files"},"totalDurationMs":{"type":"number","description":"Sum of all successful durations in milliseconds"},"totalDurationFormatted":{"type":"string","description":"Total duration formatted as HH:MM:SS.mmm"}}}}}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/tools/audio/duration/bulk":{"post":{"summary":"Get audio duration for multiple files (bulk)","description":"Calculate and return the duration of multiple audio files. Accepts an array of public URLs or internal storage paths. Maximum 5 files for free plan, 300 files for paid plans. Costs 1 token per file.","tags":["Tools","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["files"],"properties":{"files":{"type":"array","description":"Array of file paths or URLs. Can be strings or objects with path/url property. Limit: 5 files (free plan) or 300 files (paid plans).","maxItems":300,"items":{"oneOf":[{"type":"string","description":"Internal storage path (starts with /) or public URL (http/https)","example":"/audio/2024-01-15/user123/abc123.mp3"},{"type":"object","properties":{"path":{"type":"string","description":"Internal storage path"},"url":{"type":"string","description":"Public URL"}}}]}}}},"examples":{"stringArray":{"summary":"Array of strings","value":{"files":["/audio/2024-01-15/user123/audio1.mp3","https://example.com/audio2.mp3"]}},"objectArray":{"summary":"Array of objects","value":{"files":[{"path":"/audio/2024-01-15/user123/audio1.mp3"},{"url":"https://example.com/audio2.mp3"}]}}}}}},"responses":{"200":{"description":"Bulk audio duration calculation completed","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"results":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"durationMs":{"type":"number","description":"Duration in milliseconds"},"durationFormatted":{"type":"string","description":"Duration formatted as HH:MM:SS.mmm or MM:SS.mmm"},"source":{"type":"string","enum":["storage","url"]},"originalPath":{"type":"string"}}},{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"string"},"originalPath":{"type":"string"}}}]}},"summary":{"type":"object","properties":{"total":{"type":"integer","description":"Total number of files processed"},"successful":{"type":"integer","description":"Number of successfully processed files"},"failed":{"type":"integer","description":"Number of failed files"},"totalDurationMs":{"type":"number","description":"Sum of all successful durations in milliseconds"},"totalDurationFormatted":{"type":"string","description":"Total duration formatted as HH:MM:SS.mmm"}}}}}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/schemas":{"get":{"summary":"List all available schemas","description":"Get a list of all available JSON schemas with their URLs and descriptions","tags":["Schemas","userApi"],"responses":{"200":{"description":"List of available schemas","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"schemas":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"url":{"type":"string"},"usage":{"type":"string"}}}}}}}}}}}}}},"/api/v1/schemas/image":{"get":{"summary":"Get image schema","description":"Get the full JSON schema for image configuration.\nThis schema is used for validating image job configurations.\n","tags":["Schemas","userApi"],"responses":{"200":{"description":"Image schema retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"schema":{"type":"object","description":"Full JSON schema object","additionalProperties":true},"description":{"type":"string"},"version":{"type":"string"}}},"message":{"type":"string","example":"Image schema retrieved successfully"}}}}}}}}},"/api/v1/schemas/video":{"get":{"summary":"Get video schema","description":"Get the full JSON schema for video configuration.\nThis schema is used for validating video job configurations.\n","tags":["Schemas","userApi"],"responses":{"200":{"description":"Video schema retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"object","properties":{"schema":{"type":"object","description":"Full JSON schema object","additionalProperties":true},"description":{"type":"string"},"version":{"type":"string"}}},"message":{"type":"string","example":"Video schema retrieved successfully"}}}}}}}}},"/api/v1/jobs/queue-status":{"get":{"summary":"Get queue status (Admin only)","description":"Get current status of job processing queues. This is an administrative endpoint\nthat requires admin basic auth in production.\n","tags":["Jobs"],"security":[{"BasicAuth":[]}],"responses":{"200":{"description":"Queue status retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"queues":{"type":"object","properties":{"image":{"type":"object","properties":{"messageCount":{"type":"integer"},"consumerCount":{"type":"integer"}}},"video":{"type":"object","properties":{"messageCount":{"type":"integer"},"consumerCount":{"type":"integer"}}}}},"isHealthy":{"type":"boolean"}}}}}]}}}},"401":{"description":"Authentication required (admin basic auth in production)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/jobs":{"post":{"summary":"Create a new job","description":"Create and queue a new image or video generation job.\nJob priority is automatically set to 5 (medium priority) for all users.\n\n**Important:** For file paths in job configurations, use relative paths starting with '/'\n(e.g., \"/input/user123/file.jpg\") that you get from file upload responses or use public file URLs (e.g., \"https://example.com/file.jpg\") that must end with a file extension (e.g., \".jpg\").\n","tags":["Jobs","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateJobRequest"}}}},"responses":{"201":{"description":"Job created successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"jobId":{"type":"string","description":"Unique identifier for the created job"},"status":{"type":"string","enum":["PENDING","PROCESSING","COMPLETED","FAILED","CANCELLED"],"description":"Initial job status (always PENDING)"},"type":{"type":"string","enum":["IMAGE","VIDEO"],"description":"Type of job created"},"estimatedTokens":{"type":"integer","description":"Estimated token cost (final cost calculated upon completion)"},"createdAt":{"type":"string","format":"date-time","description":"Timestamp when the job was created"}}}}}]}}}},"400":{"description":"Invalid job configuration","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false,"description":"Always false for errors"},"error":{"type":"string","description":"Error message"},"details":{"type":"object","properties":{"errors":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"Field that caused the error"},"message":{"type":"string","description":"Human-readable error message"},"code":{"type":"string","description":"Machine-readable error code (e.g., INVALID_JOB_TYPE, INVALID_CONFIG_TYPE)"}}}}}}},"required":["success","error"]},"examples":{"invalidType":{"summary":"Invalid job type","value":{"success":false,"error":"Job configuration is invalid","details":{"errors":[{"field":"type","message":"Invalid job type. Must be \"image\" or \"video\"","code":"INVALID_JOB_TYPE"}]}}},"invalidConfig":{"summary":"Invalid config structure","value":{"success":false,"error":"Job configuration is invalid","details":{"errors":[{"field":"config","message":"config must be an object","code":"INVALID_CONFIG_TYPE"}]}}},"validationErrors":{"summary":"Schema validation errors","value":{"success":false,"error":"Job configuration is invalid","details":{"errors":[{"field":"config.width","message":"must be number"},{"field":"config.layers","message":"must be array"}]}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Insufficient tokens","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false,"description":"Always false for errors"},"error":{"type":"string","description":"Error message"}},"required":["success","error"]}}}},"429":{"description":"Too many concurrent jobs","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false,"description":"Always false for errors"},"error":{"type":"string","description":"Error message about concurrent job limit"},"details":{"type":"object","properties":{"activeJobs":{"type":"integer","description":"Number of currently active jobs"},"maxAllowed":{"type":"integer","description":"Maximum allowed concurrent jobs"}}}},"required":["success","error"]}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}},"get":{"summary":"List user jobs","description":"Get a paginated list of jobs for the authenticated user.\n\n**File Downloads:**\n- Use `inputFileIds` array to download input files via `/api/v1/files/{fileId}/download`\n- Use `outputFileId` to download the output file when job is completed\n- All file IDs reference MediaFile records that can be downloaded securely\n","tags":["Jobs","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"parameters":[{"in":"query","name":"page","schema":{"type":"integer","minimum":1,"default":1},"description":"Page number for pagination"},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Number of items per page"},{"in":"query","name":"status","schema":{"type":"string","enum":["PENDING","PROCESSING","COMPLETED","FAILED","CANCELLED"]},"description":"Filter by job status"},{"in":"query","name":"type","schema":{"type":"string","enum":["IMAGE","VIDEO"]},"description":"Filter by job type"},{"in":"query","name":"sortBy","schema":{"type":"string","enum":["createdAt","updatedAt","startedAt","completedAt"],"default":"createdAt"},"description":"Field to sort by"},{"in":"query","name":"sortOrder","schema":{"type":"string","enum":["asc","desc"],"default":"desc"},"description":"Sort order"}],"responses":{"200":{"description":"Jobs retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"jobs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Job ID"},"type":{"type":"string","enum":["IMAGE","VIDEO"],"description":"Job type"},"status":{"type":"string","enum":["PENDING","PROCESSING","COMPLETED","FAILED","CANCELLED"],"description":"Current job status"},"outputFormat":{"type":"string","description":"Output format (png, mp4, etc.)"},"outputFileId":{"type":"string","nullable":true,"description":"Output MediaFile ID (use this for download when job is completed)"},"tokensUsed":{"type":"integer","description":"Tokens consumed by this job"},"errorMessage":{"type":"string","nullable":true,"description":"Error message if job failed"},"startedAt":{"type":"string","format":"date-time","nullable":true,"description":"When job processing started"},"completedAt":{"type":"string","format":"date-time","nullable":true,"description":"When job processing completed"},"createdAt":{"type":"string","format":"date-time","description":"When job was created"},"updatedAt":{"type":"string","format":"date-time","description":"When job was last updated"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer","description":"Current page number"},"limit":{"type":"integer","description":"Number of items per page"},"totalCount":{"type":"integer","description":"Total number of jobs"},"totalPages":{"type":"integer","description":"Total number of pages"},"hasNextPage":{"type":"boolean","description":"Whether there are more pages"},"hasPrevPage":{"type":"boolean","description":"Whether there are previous pages"}}}}}}}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/jobs/validate":{"post":{"summary":"Validate job configuration","description":"Validate a job configuration without creating the job.\n\n**Important:** This endpoint always returns HTTP 200 with a validation result.\nCheck the `isValid` field in the response to determine if the configuration is valid.\nPre-validation checks (type and config structure) are performed before schema validation.\n","tags":["Jobs","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateJobRequest"}}}},"responses":{"200":{"description":"Job configuration validated","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"isValid":{"type":"boolean","description":"Whether the job configuration is valid"},"errors":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","description":"The field that has an error (optional)"},"message":{"type":"string","description":"Human-readable error message"},"code":{"type":"string","description":"Machine-readable error code (e.g., INVALID_JOB_TYPE, INVALID_CONFIG_TYPE, VALIDATION_ERROR)"}}},"description":"Array of validation errors (empty if valid)","example":[{"field":"type","message":"Invalid job type. Must be \"image\" or \"video\"","code":"INVALID_JOB_TYPE"},{"field":"config.width","message":"must be number","code":"VALIDATION_ERROR"}]},"estimatedTokens":{"type":"integer","description":"Estimated token cost for the job (final cost may differ)"},"detectedResources":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["image","video","audio","font","unknown"],"description":"Type of detected resource"},"url":{"type":"string","description":"Resource URL or path"},"size":{"type":"integer","description":"Resource size in bytes (if available)"}}},"description":"Resources detected in the job configuration"}}}}}]},"examples":{"validConfig":{"summary":"Valid configuration","value":{"success":true,"data":{"isValid":true,"errors":[],"estimatedTokens":150,"detectedResources":[{"type":"image","url":"/input/user123/background.jpg"}]},"message":"Job configuration validated"}},"invalidType":{"summary":"Invalid job type","value":{"success":true,"data":{"isValid":false,"errors":[{"field":"type","message":"Invalid job type. Must be \"image\" or \"video\"","code":"INVALID_JOB_TYPE"}],"estimatedTokens":0,"detectedResources":[]},"message":"Job configuration validated"}},"invalidConfig":{"summary":"Invalid configuration structure","value":{"success":true,"data":{"isValid":false,"errors":[{"field":"config","message":"config must be an object","code":"INVALID_CONFIG_TYPE"}],"estimatedTokens":0,"detectedResources":[]},"message":"Job configuration validated"}},"schemaValidationError":{"summary":"Schema validation errors","value":{"success":true,"data":{"isValid":false,"errors":[{"field":"config.width","message":"must be number"},{"field":"config.layers","message":"must be array"}],"estimatedTokens":0,"detectedResources":[]},"message":"Job configuration validated"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/jobs/stats":{"get":{"summary":"Get job statistics","description":"Get statistics about jobs for the authenticated user","tags":["Jobs"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"responses":{"200":{"description":"Job statistics retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"total":{"type":"integer","description":"Total number of jobs for the user"},"totalTokensUsed":{"type":"integer","description":"Total tokens consumed across all jobs"},"byStatus":{"type":"object","description":"Job counts grouped by status","properties":{"PENDING":{"type":"integer","description":"Number of pending jobs"},"PROCESSING":{"type":"integer","description":"Number of processing jobs"},"COMPLETED":{"type":"integer","description":"Number of completed jobs"},"FAILED":{"type":"integer","description":"Number of failed jobs"},"CANCELLED":{"type":"integer","description":"Number of cancelled jobs"}}},"byType":{"type":"object","description":"Job counts grouped by type","properties":{"IMAGE":{"type":"integer","description":"Number of image jobs"},"VIDEO":{"type":"integer","description":"Number of video jobs"}}}}}}}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/jobs/stats/last-30-days":{"get":{"summary":"Get job statistics for last 30 days","description":"Get daily job statistics (success/failed) for the last 30 days for the authenticated user","tags":["Jobs"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"responses":{"200":{"description":"Job statistics for last 30 days retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date","description":"Date in YYYY-MM-DD format"},"success":{"type":"integer","description":"Number of successful jobs on this date"},"failed":{"type":"integer","description":"Number of failed jobs on this date"}}},"example":[{"date":"2024-01-01","success":5,"failed":1},{"date":"2024-01-02","success":3,"failed":0}]}}}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/jobs/{jobId}":{"get":{"summary":"Get job details","description":"Retrieve details of a specific job.\n\n**File Downloads:**\n- Use `inputFileIds` array to download input files via `/api/v1/files/{fileId}/download`\n- Use `outputFileId` to download the output file when job is completed\n- All file IDs reference MediaFile records that can be downloaded securely\n","tags":["Jobs","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"parameters":[{"in":"path","name":"jobId","required":true,"schema":{"type":"string"},"description":"Job ID"}],"responses":{"200":{"description":"Job details retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Job ID"},"type":{"type":"string","enum":["IMAGE","VIDEO"],"description":"Job type"},"status":{"type":"string","enum":["PENDING","PROCESSING","COMPLETED","FAILED","CANCELLED"],"description":"Current job status"},"config":{"type":"object","description":"Job configuration as provided when creating the job"},"inputFileIds":{"type":"array","items":{"type":"string"},"description":"Array of input MediaFile IDs (use these for downloads)"},"outputFormat":{"type":"string","description":"Output format (png, mp4, etc.)"},"outputFileId":{"type":"string","nullable":true,"description":"Output MediaFile ID (use this for download when job is completed)"},"tokensUsed":{"type":"integer","description":"Tokens consumed by this job (0 until completion)"},"errorMessage":{"type":"string","nullable":true,"description":"Error message if job failed"},"startedAt":{"type":"string","format":"date-time","nullable":true,"description":"When job processing started"},"completedAt":{"type":"string","format":"date-time","nullable":true,"description":"When job processing completed"},"createdAt":{"type":"string","format":"date-time","description":"When job was created"},"updatedAt":{"type":"string","format":"date-time","description":"When job was last updated"}}}}}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalServerError"}}},"delete":{"summary":"Cancel a job","description":"Cancel a pending job (only works for jobs in PENDING status)","tags":["Jobs","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"parameters":[{"in":"path","name":"jobId","required":true,"schema":{"type":"string"},"description":"Job ID to cancel"}],"responses":{"200":{"description":"Job cancelled successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Job ID"},"status":{"type":"string","enum":["CANCELLED"],"description":"Updated job status (always CANCELLED for successful cancellation)"},"updatedAt":{"type":"string","format":"date-time","description":"Timestamp when the job was cancelled"}}}}}]}}}},"400":{"description":"Job cannot be cancelled (wrong status)","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false,"description":"Always false for errors"},"error":{"type":"string","description":"Error message"}},"required":["success","error"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/health":{"get":{"summary":"Basic health check","description":"Basic health check endpoint to verify the service is running","tags":["Health"],"responses":{"200":{"description":"Service is healthy","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string","example":"healthy"},"timestamp":{"type":"string","format":"date-time"},"service":{"type":"string","example":"media-core"},"version":{"type":"string","example":"1.0.0"},"environment":{"type":"string","example":"development"}}}}}]}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/health/detailed":{"get":{"summary":"Detailed health check","description":"Comprehensive health check with dependency status. Requires admin authentication in production.","tags":["Health"],"security":[{"BasicAuth":[]}],"responses":{"200":{"description":"Detailed health status","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string","enum":["healthy","warning","unhealthy"]},"timestamp":{"type":"string","format":"date-time"},"service":{"type":"string"},"version":{"type":"string"},"environment":{"type":"string"},"checks":{"type":"object","properties":{"database":{"type":"object","properties":{"status":{"type":"string","enum":["healthy","unhealthy"]},"responseTime":{"type":"number"},"error":{"type":"string"}}},"memory":{"type":"object","properties":{"status":{"type":"string","enum":["healthy","warning"]},"usage":{"type":"number"},"limit":{"type":"number"}}},"uptime":{"type":"number"}}}}}}}]}}}},"401":{"description":"Authentication required (production only)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Service is unhealthy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/health/ready":{"get":{"summary":"Readiness check","description":"Check if the service is ready to accept requests (for Kubernetes/Docker). Requires admin authentication in production.","tags":["Health"],"security":[{"BasicAuth":[]}],"responses":{"200":{"description":"Service is ready","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string","example":"ready"},"timestamp":{"type":"string","format":"date-time"}}}}}]}}}},"401":{"description":"Authentication required (production only)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Service is not ready","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/health/live":{"get":{"summary":"Liveness check","description":"Check if the service is alive (for Kubernetes/Docker). Requires admin authentication in production.","tags":["Health"],"security":[{"BasicAuth":[]}],"responses":{"200":{"description":"Service is alive","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string","example":"alive"},"timestamp":{"type":"string","format":"date-time"},"uptime":{"type":"number","description":"Process uptime in seconds"}}}}}]}}}},"401":{"description":"Authentication required (production only)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/health/metrics":{"get":{"summary":"System metrics","description":"Get detailed system and application metrics for monitoring. Requires admin authentication in production.","tags":["Health"],"security":[{"BasicAuth":[]}],"responses":{"200":{"description":"System metrics retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"timestamp":{"type":"string","format":"date-time"},"process":{"type":"object","properties":{"uptime":{"type":"number"},"memory":{"type":"object","properties":{"rss":{"type":"number"},"heapTotal":{"type":"number"},"heapUsed":{"type":"number"},"external":{"type":"number"},"arrayBuffers":{"type":"number"}}},"cpu":{"type":"object","properties":{"user":{"type":"number"},"system":{"type":"number"}}}}},"database":{"type":"object","properties":{"users":{"type":"number"},"activeApiKeys":{"type":"number"},"totalJobs":{"type":"number"},"jobsByStatus":{"type":"object","additionalProperties":{"type":"number"}}}}}}}}]}}}},"401":{"description":"Authentication required (production only)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/health/job-cleanup":{"get":{"summary":"Job cleanup service status","description":"Get the current status and statistics of the job cleanup service. Requires admin authentication in production.","tags":["Health"],"security":[{"BasicAuth":[]}],"responses":{"200":{"description":"Job cleanup service status","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"service":{"type":"string","example":"job-cleanup"},"status":{"type":"string","enum":["running","stopped"]},"configuration":{"type":"object","properties":{"intervalMinutes":{"type":"number","example":30},"timeoutMinutes":{"type":"number","example":30},"nextCleanupInMs":{"type":"number"}}},"statistics":{"type":"object","properties":{"totalJobs":{"type":"number"},"pendingJobs":{"type":"number"},"processingJobs":{"type":"number"},"completedJobs":{"type":"number"},"failedJobs":{"type":"number"},"cancelledJobs":{"type":"number"},"staleJobsToCleanup":{"type":"number"}}},"timestamp":{"type":"string","format":"date-time"}}}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/health/job-cleanup/run":{"post":{"summary":"Run job cleanup manually","description":"Manually trigger the job cleanup process to clean up stale jobs. Requires admin authentication in production.","tags":["Health"],"security":[{"BasicAuth":[]}],"responses":{"200":{"description":"Job cleanup completed successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"message":{"type":"string","example":"Job cleanup completed successfully"},"result":{"type":"object","properties":{"pendingJobs":{"type":"number","description":"Number of PENDING jobs cleaned up"},"processingJobs":{"type":"number","description":"Number of PROCESSING jobs cleaned up"},"total":{"type":"number","description":"Total jobs cleaned up"},"duration":{"type":"number","description":"Duration of cleanup in milliseconds"}}},"timestamp":{"type":"string","format":"date-time"}}}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/files/public/{token}":{"get":{"summary":"Download file via public link","description":"Download a file using a time-limited public token. No authentication required.","tags":["Files","userApi"],"parameters":[{"in":"path","name":"token","required":true,"schema":{"type":"string"},"description":"The JWT token from the public download link"}],"responses":{"200":{"description":"File downloaded successfully","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}},"headers":{"Content-Disposition":{"description":"Attachment with original filename","schema":{"type":"string"}},"Content-Type":{"description":"The MIME type of the file","schema":{"type":"string"}},"Content-Length":{"description":"The size of the file in bytes","schema":{"type":"integer"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"description":"Invalid or malformed token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"410":{"description":"Download link has expired or file has been deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/files/upload":{"post":{"summary":"Upload a file","description":"Upload a media file (image, video, audio, or font) to the platform","tags":["Files","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"The file to upload"},"category":{"type":"string","enum":["image","video","audio","font"],"description":"Category of the file being uploaded"},"ttlHours":{"type":"integer","minimum":1,"maximum":48,"default":1,"description":"Time to live in hours (1-48). File will be automatically deleted after this period."}},"required":["file","category"]}}}},"responses":{"201":{"description":"File uploaded successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/UploadFileResponse"}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"413":{"description":"File too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"File validation failed or unsupported file type","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"error":{"type":"string","examples":["File validation failed: File type 'png' does not match expected category 'video'. Detected category: 'image'","Unsupported file type: svg. Supported types: png, jpeg, jpg, gif, webp, mp4, mov, avi, webm, mp3, wav, m4a, aac, ttf, otf, woff, woff2"]}}}]}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/files/validate/image":{"post":{"summary":"Validate an image file","description":"Validate an image file's format and metadata without uploading it","tags":["Files","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"The image file to validate"}},"required":["file"]}}}},"responses":{"200":{"description":"Image validation completed","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ValidationApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"validation":{"type":"object","properties":{"isValid":{"type":"boolean","description":"Whether the file validation passed"},"fileSize":{"type":"integer","description":"File size in bytes"},"fileType":{"type":"string","description":"Detected file extension"},"detectedMimeType":{"type":"string","description":"Detected MIME type"},"errors":{"type":"array","items":{"type":"string"},"description":"Validation error messages"},"category":{"type":"string","enum":["image","video","audio","font","unknown"],"description":"Detected file category"}}},"uploadedFile":{"type":"object","properties":{"originalName":{"type":"string","description":"Original filename"},"mimetype":{"type":"string","description":"Original MIME type"},"size":{"type":"integer","description":"File size in bytes"}}}}}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"description":"Unsupported file type","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"error":{"type":"string","example":"Unsupported file type: svg. Supported types: png, jpeg, jpg, gif, webp, mp4, mov, avi, webm, mp3, wav, m4a, aac, ttf, otf, woff, woff2"}}}]}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/files/validate/video":{"post":{"summary":"Validate a video file","description":"Validate a video file's format and metadata without uploading it","tags":["Files","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"The video file to validate"}},"required":["file"]}}}},"responses":{"200":{"description":"Video validation completed","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ValidationApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"validation":{"type":"object","properties":{"isValid":{"type":"boolean","description":"Whether the file validation passed"},"fileSize":{"type":"integer","description":"File size in bytes"},"fileType":{"type":"string","description":"Detected file extension"},"detectedMimeType":{"type":"string","description":"Detected MIME type"},"errors":{"type":"array","items":{"type":"string"},"description":"Validation error messages"},"category":{"type":"string","enum":["image","video","audio","font","unknown"],"description":"Detected file category"}}},"uploadedFile":{"type":"object","properties":{"originalName":{"type":"string","description":"Original filename"},"mimetype":{"type":"string","description":"Original MIME type"},"size":{"type":"integer","description":"File size in bytes"}}}}}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"description":"Unsupported file type","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"error":{"type":"string","example":"Unsupported file type: svg. Supported types: png, jpeg, jpg, gif, webp, mp4, mov, avi, webm, mp3, wav, m4a, aac, ttf, otf, woff, woff2"}}}]}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/files/validate/audio":{"post":{"summary":"Validate an audio file","description":"Validate an audio file's format and metadata without uploading it","tags":["Files","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"requestBody":{"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"The audio file to validate"}},"required":["file"]}}}},"responses":{"200":{"description":"Audio validation completed","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ValidationApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"validation":{"type":"object","properties":{"isValid":{"type":"boolean","description":"Whether the file validation passed"},"fileSize":{"type":"integer","description":"File size in bytes"},"fileType":{"type":"string","description":"Detected file extension"},"detectedMimeType":{"type":"string","description":"Detected MIME type"},"errors":{"type":"array","items":{"type":"string"},"description":"Validation error messages"},"category":{"type":"string","enum":["image","video","audio","font","unknown"],"description":"Detected file category"}}},"uploadedFile":{"type":"object","properties":{"originalName":{"type":"string","description":"Original filename"},"mimetype":{"type":"string","description":"Original MIME type"},"size":{"type":"integer","description":"File size in bytes"}}}}}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"description":"Unsupported file type","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"error":{"type":"string","example":"Unsupported file type: svg. Supported types: png, jpeg, jpg, gif, webp, mp4, mov, avi, webm, mp3, wav, m4a, aac, ttf, otf, woff, woff2"}}}]}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/files/supported-types":{"get":{"summary":"Get supported file types","description":"Retrieve a list of all supported file types grouped by category","tags":["Files","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"responses":{"200":{"description":"Supported file types retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"image":{"type":"array","items":{"type":"string"}},"video":{"type":"array","items":{"type":"string"}},"audio":{"type":"array","items":{"type":"string"}},"font":{"type":"array","items":{"type":"string"}}}}}}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/files/{fileId}/download":{"get":{"summary":"Download a file","description":"Download a file by its ID. Only the user who uploaded the file can download it.","tags":["Files","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"parameters":[{"in":"path","name":"fileId","required":true,"schema":{"type":"string"},"description":"The ID of the file to download"}],"responses":{"200":{"description":"File downloaded successfully","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}},"headers":{"Content-Disposition":{"description":"Attachment with original filename","schema":{"type":"string"}},"Content-Type":{"description":"The MIME type of the file","schema":{"type":"string"}},"Content-Length":{"description":"The size of the file in bytes","schema":{"type":"integer"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Access denied - not the file owner","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"410":{"description":"File has been deleted and is no longer available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/files/{fileId}/public-link":{"post":{"summary":"Generate public download link","description":"Generate a time-limited public download link for a file. Only the file owner can generate links.","tags":["Files","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"parameters":[{"in":"path","name":"fileId","required":true,"schema":{"type":"string"},"description":"The ID of the file"}],"responses":{"200":{"description":"Public download link generated successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"publicUrl":{"type":"string","description":"The public download URL","example":"http://localhost:3001/api/v1/files/public/ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5..."},"expiresAt":{"type":"string","format":"date-time","description":"Timestamp when the link expires"},"expiresInMinutes":{"type":"integer","description":"Number of minutes until expiration","example":60}}}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Access denied - not the file owner","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"410":{"description":"File has been deleted and is no longer available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/files/{fileId}":{"get":{"summary":"Get file metadata","description":"Retrieve metadata for a specific file","tags":["Files","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"parameters":[{"in":"path","name":"fileId","required":true,"schema":{"type":"string"},"description":"The ID of the file"}],"responses":{"200":{"description":"File metadata retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"storageUrl":{"type":"string","description":"Relative path to use in job configurations"},"metadata":{"type":"object","description":"File metadata including validation results","properties":{"category":{"type":"string","enum":["image","video","audio","font","output"],"description":"Category of the file"},"uploadedAt":{"type":"string","format":"date-time","description":"Timestamp when the file was uploaded"},"validationResult":{"type":"object","properties":{"errors":{"type":"array","items":{"type":"string"},"description":"Validation error messages"},"isValid":{"type":"boolean","description":"Whether file validation passed"},"fileSize":{"type":"integer","description":"File size in bytes"},"detectedMimeType":{"type":"string","description":"Detected MIME type"}}}}},"updatedAt":{"type":"string","format":"date-time","description":"Timestamp when the file was last updated"}}}}}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Access denied - not the file owner","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"410":{"description":"File has been deleted and is no longer available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}},"delete":{"summary":"Delete a file","description":"Delete a file and its associated metadata. Only the file owner can delete it.","tags":["Files","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"parameters":[{"in":"path","name":"fileId","required":true,"schema":{"type":"string"},"description":"The ID of the file to delete"}],"responses":{"204":{"description":"File deleted successfully"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Access denied - not the file owner","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/files":{"get":{"summary":"List user files","description":"Get a paginated list of files uploaded by the authenticated user","tags":["Files","userApi"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"parameters":[{"in":"query","name":"page","schema":{"type":"integer","minimum":1,"default":1},"description":"Page number for pagination"},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Number of items per page"},{"in":"query","name":"category","schema":{"type":"string","enum":["image","video","audio","font"]},"description":"Filter by file category"},{"in":"query","name":"includeOutput","schema":{"type":"boolean","default":false},"description":"Include output files in results - default false, only input files"}],"responses":{"200":{"description":"Files retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ValidationApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"files":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier for the file"},"filename":{"type":"string","description":"Generated filename on the server"},"originalName":{"type":"string","description":"Original filename as uploaded by user"},"mimeType":{"type":"string","description":"MIME type of the file"},"storageUrl":{"type":"string","description":"Relative path to use in job configurations"},"metadata":{"type":"object","description":"File metadata including validation results","properties":{"category":{"type":"string","enum":["image","video","audio","font","output"],"description":"Category of the file"},"validationResult":{"type":"object","properties":{"errors":{"type":"array","items":{"type":"string"},"description":"Validation error messages"},"isValid":{"type":"boolean","description":"Whether file validation passed"},"fileSize":{"type":"integer","description":"File size in bytes"},"detectedMimeType":{"type":"string","description":"Detected MIME type"}}}}},"expiresAt":{"type":"string","format":"date-time","description":"Timestamp when the file will be automatically deleted"},"createdAt":{"type":"string","format":"date-time","description":"Timestamp when the file was uploaded"},"updatedAt":{"type":"string","format":"date-time","description":"Timestamp when the file was last updated"}}}},"pagination":{"type":"object","properties":{"page":{"type":"integer","minimum":1,"description":"Current page number"},"limit":{"type":"integer","minimum":1,"maximum":100,"description":"Number of items per page"},"total":{"type":"integer","minimum":0,"description":"Total number of files"},"totalPages":{"type":"integer","minimum":0,"description":"Total number of pages"}}}}}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/files/{fileId}/stream":{"get":{"summary":"Stream file directly","description":"Stream the file content directly to the user. File is served with appropriate headers for browser display/download.","tags":["Files"],"security":[{"BearerAuth":[]},{"ApiKeyAuth":[]}],"parameters":[{"in":"path","name":"fileId","required":true,"schema":{"type":"string"},"description":"The ID of the file to stream"}],"responses":{"200":{"description":"File streamed successfully","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}},"headers":{"Content-Type":{"description":"The MIME type of the file","schema":{"type":"string"}},"Content-Disposition":{"description":"Inline with original filename","schema":{"type":"string"}},"Content-Length":{"description":"The size of the file in bytes","schema":{"type":"integer"}},"Cache-Control":{"description":"Cache control header","schema":{"type":"string","example":"public, max-age=3600"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"description":"File has been deleted and is no longer available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/billing/plans":{"get":{"summary":"Get available billing plans (Public)","description":"Retrieve all available billing plans. This endpoint is public and does not require authentication.","tags":["Billing"],"responses":{"200":{"description":"Available plans retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"plans":{"type":"array","items":{"$ref":"#/components/schemas/BillingPlan"}}}}}}]}}}}}}},"/api/v1/billing/plan":{"get":{"summary":"Get current billing plan and usage","description":"Retrieve the authenticated user's current billing plan and token usage information","tags":["Billing"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Current plan and usage retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CurrentPlanResponse"}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/billing/checkout":{"post":{"summary":"Create Stripe Checkout Session","description":"Create a Stripe checkout session for plan subscription","tags":["Billing"],"security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["planId","billingPeriod"],"properties":{"planId":{"type":"string","description":"ID of the plan to subscribe to"},"billingPeriod":{"type":"string","enum":["monthly","yearly"],"description":"Billing period"},"successUrl":{"type":"string","description":"URL to redirect after successful payment"},"cancelUrl":{"type":"string","description":"URL to redirect after cancelled payment"}}}}}}}},"/api/v1/billing/portal":{"post":{"summary":"Create Stripe Customer Portal Session","description":"Create a session to manage subscription in Stripe Customer Portal","tags":["Billing"],"security":[{"BearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"returnUrl":{"type":"string","description":"URL to return to after portal session"}}}}}}}},"/api/v1/billing/usage/history":{"get":{"summary":"Get monthly usage history","description":"Retrieve historical monthly usage data for the authenticated user","tags":["Billing"],"security":[{"BearerAuth":[]}],"parameters":[{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":50,"default":12},"description":"Number of months to retrieve"},{"in":"query","name":"offset","schema":{"type":"integer","minimum":0,"default":0},"description":"Number of months to skip for pagination"},{"in":"query","name":"startYear","schema":{"type":"integer","minimum":2020},"description":"Start year for filtering"},{"in":"query","name":"endYear","schema":{"type":"integer","minimum":2020},"description":"End year for filtering"}],"responses":{"200":{"description":"Usage history retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"usage":{"type":"array","items":{"type":"object","properties":{"year":{"type":"integer"},"month":{"type":"integer"},"tokensUsed":{"type":"integer"},"tokensQuota":{"type":"integer"},"planName":{"type":"string"},"totalJobs":{"type":"integer"},"completedJobs":{"type":"integer"},"failedJobs":{"type":"integer"}}}},"pagination":{"type":"object","properties":{"limit":{"type":"integer"},"offset":{"type":"integer"},"total":{"type":"integer"},"hasMore":{"type":"boolean"}}}}}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/billing/payments":{"get":{"summary":"Get payment history","description":"Retrieve payment history for the authenticated user","tags":["Billing"],"security":[{"BearerAuth":[]}],"parameters":[{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"description":"Number of payments to retrieve"},{"in":"query","name":"offset","schema":{"type":"integer","minimum":0,"default":0},"description":"Number of payments to skip for pagination"},{"in":"query","name":"status","schema":{"type":"string","enum":["PENDING","PROCESSING","COMPLETED","FAILED","REFUNDED","CANCELLED"]},"description":"Filter by payment status"}],"responses":{"200":{"description":"Payment history retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"payments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"amount":{"type":"number"},"currency":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"description":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}}}}}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/billing/usage/daily":{"get":{"summary":"Get daily token usage for last 30 days","description":"Retrieve daily token consumption data for the last 30 days for charts","tags":["Billing"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Daily token usage data retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date","description":"Date in YYYY-MM-DD format"},"tokens":{"type":"integer","minimum":0,"description":"Number of tokens used on this date"},"formattedDate":{"type":"string","description":"Human-readable date format (e.g., \"Jan 15\")"}}}}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/billing/period/check":{"post":{"summary":"Force check billing period","description":"Manually trigger a billing period check and reset if expired.\nUseful for testing or administrative purposes.\n","tags":["Billing"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"Period check completed","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"periodReset":{"type":"boolean"},"previousUsage":{"type":"integer"},"newPeriodStart":{"type":"string","format":"date-time"},"newPeriodEnd":{"type":"string","format":"date-time"},"message":{"type":"string"}}}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/auth/register":{"post":{"summary":"Register a new user","description":"Create a new user account with email and optional name.\n\n**hCaptcha Verification:** When hCaptcha is enabled, the hcaptchaToken field is required for verification before account creation.\n","tags":["Authentication"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterRequest"}}}},"responses":{"201":{"description":"User registered successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/User"}}}]}}}},"409":{"description":"User with this email already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error or hCaptcha verification failed","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"message":{"type":"string","enum":["Email is required","Please provide a valid email address","hCaptcha verification is required","hCaptcha verification failed"]}}}]}}}},"429":{"$ref":"#/components/responses/RateLimitError"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/auth/login":{"post":{"summary":"Initiate login process","description":"Generate 6-digit numeric login code for authentication.\n- In development: Returns the code in response for testing\n- In production: Sends code via email and only returns expiration time\n\n**hCaptcha Verification:** When hCaptcha is enabled, the hcaptchaToken field is required for verification before sending login code.\n\n**Rate Limiting:** In production, only one login code can be requested every 3 minutes per email address.\n","tags":["Authentication"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"6-digit login code generated.\n- Development: Returns code in response\n- Production: Sends code via email, only returns expiration time\n","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"message":{"type":"string","description":"- Development: \"6-digit login code generated\"\n- Production: \"6-digit login code sent\"\n"},"token":{"type":"string","description":"6-digit numeric login code (development environment only)"},"expiresAt":{"type":"string","format":"date-time","description":"When the 6-digit login code expires"}}}}}]}}}},"403":{"description":"Account is inactive","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error or hCaptcha verification failed","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"message":{"type":"string","enum":["Email is required","Please provide a valid email address","hCaptcha verification is required","hCaptcha verification failed"]}}}]}}}},"429":{"description":"Rate limit exceeded (must wait 3 minutes between login requests)","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"data":{"type":"object","properties":{"nextAllowedAt":{"type":"string","format":"date-time","description":"When the next login request can be made"},"message":{"type":"string","description":"Detailed rate limit message"}}}}}]}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/auth/verify-token":{"post":{"summary":"Verify authentication token","description":"Verify authentication token and receive JWT for dashboard access","tags":["Authentication"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyTokenRequest"}}}},"responses":{"200":{"description":"Token verified successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"token":{"type":"string","description":"JWT token for dashboard access"}}}}}]}}}},"401":{"description":"Invalid or expired authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Account is inactive","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimitError"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/auth/resend-verification":{"post":{"summary":"Resend verification email","description":"Resend verification email to user. Rate limited to one email every 3 minutes.\nOnly works for unverified users.\n","tags":["Authentication"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"Verification email sent (if user exists and is unverified)","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"message":{"type":"string","example":"Verification email sent"},"email":{"type":"string","format":"email"}}}}}]}}}},"400":{"description":"User already verified or other validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"User not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"description":"Rate limit exceeded (must wait 3 minutes between emails)","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"data":{"type":"object","properties":{"nextAllowedAt":{"type":"string","format":"date-time","description":"When the next email can be sent"},"message":{"type":"string","description":"Detailed rate limit message"}}}}}]}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/auth/profile":{"get":{"summary":"Get user profile","description":"Retrieve the authenticated user's profile information","tags":["Authentication"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"User profile retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/User"}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"404":{"$ref":"#/components/responses/NotFoundError"},"500":{"$ref":"#/components/responses/InternalServerError"}}},"put":{"summary":"Update user profile","description":"Update the authenticated user's profile information","tags":["Authentication"],"security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProfileRequest"}}}},"responses":{"200":{"description":"Profile updated successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"},"updatedAt":{"type":"string","format":"date-time"}}}}}]}}}},"400":{"description":"No valid updates provided","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"409":{"description":"Email already in use by another account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/auth/stats":{"get":{"summary":"Get user statistics","description":"Retrieve usage statistics for the authenticated user","tags":["Authentication"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"User statistics retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"totalJobs":{"type":"integer","minimum":0},"completedJobs":{"type":"integer","minimum":0},"failedJobs":{"type":"integer","minimum":0},"usageTokens":{"type":"integer","minimum":0},"usageQuota":{"type":"integer","minimum":0},"apiKeysCount":{"type":"integer","minimum":0}}}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/auth/captcha-config":{"get":{"summary":"Get hCaptcha configuration","description":"Returns hCaptcha configuration for frontend integration","tags":["Authentication"],"responses":{"200":{"description":"hCaptcha configuration retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Whether hCaptcha is enabled"},"siteKey":{"type":"string","description":"hCaptcha site key for frontend integration"}}}}}]}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/auth/google":{"post":{"summary":"Authenticate with Google OAuth","description":"Authenticate user with Google OAuth via Supabase.\nThis endpoint handles both registration and login for Google users.\nReturns JWT token for dashboard access.\n","tags":["Authentication"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["idToken"],"properties":{"idToken":{"type":"string","description":"Supabase access token from frontend"}},"example":{"idToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}}}}},"responses":{"200":{"description":"Google authentication successful","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"token":{"type":"string","description":"JWT token for dashboard access"},"isNewUser":{"type":"boolean","description":"Whether this is a new user registration"}}}}}]}}}},"401":{"description":"Invalid Supabase access token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Missing or invalid accessToken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalServerError"},"503":{"description":"Google authentication not enabled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/supabase-config":{"get":{"summary":"Get Supabase configuration","description":"Returns Supabase configuration for frontend Google OAuth integration","tags":["Authentication"],"responses":{"200":{"description":"Supabase configuration retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Whether Supabase Google auth is enabled"},"url":{"type":"string","description":"Supabase project URL"},"anonKey":{"type":"string","description":"Supabase anonymous key for frontend"}}}}}]}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/api-keys":{"post":{"summary":"Create a new API key","description":"Create a new API key with specified permissions for programmatic access","tags":["API Keys"],"security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}}},"responses":{"201":{"description":"API key created successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"allOf":[{"$ref":"#/components/schemas/ApiKey"},{"type":"object","properties":{"key":{"type":"string","description":"The actual API key (only returned on creation)"}}}]}}}]}}}},"400":{"description":"Invalid permissions provided","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"$ref":"#/components/responses/RateLimitError"},"500":{"$ref":"#/components/responses/InternalServerError"}}},"get":{"summary":"List API keys","description":"Get all API keys for the authenticated user","tags":["API Keys"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"API keys retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiKey"}}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/api-keys/{apiKeyId}":{"get":{"summary":"Get API key details","description":"Retrieve details of a specific API key","tags":["API Keys"],"security":[{"BearerAuth":[]}],"parameters":[{"in":"path","name":"apiKeyId","required":true,"schema":{"type":"string"},"description":"API key ID"}],"responses":{"200":{"description":"API key details retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ApiKey"}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"404":{"$ref":"#/components/responses/NotFoundError"},"500":{"$ref":"#/components/responses/InternalServerError"}}},"delete":{"summary":"Delete API key","description":"Permanently delete an API key","tags":["API Keys"],"security":[{"BearerAuth":[]}],"parameters":[{"in":"path","name":"apiKeyId","required":true,"schema":{"type":"string"},"description":"API key ID"}],"responses":{"204":{"description":"API key deleted successfully"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"404":{"$ref":"#/components/responses/NotFoundError"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/api-keys/{apiKeyId}/stats":{"get":{"summary":"Get API key statistics","description":"Retrieve usage statistics for a specific API key","tags":["API Keys"],"security":[{"BearerAuth":[]}],"parameters":[{"in":"path","name":"apiKeyId","required":true,"schema":{"type":"string"},"description":"API key ID"}],"responses":{"200":{"description":"API key statistics retrieved successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ApiResponse"},{"type":"object","properties":{"data":{"type":"object","properties":{"usageCount":{"type":"integer","minimum":0,"description":"Number of times the API key was used"},"usageTokens":{"type":"integer","minimum":0,"description":"Total tokens consumed via this API key"},"lastUsedAt":{"type":"string","format":"date-time","nullable":true,"description":"Last time the API key was used"},"totalJobs":{"type":"integer","minimum":0,"description":"Total jobs created by the user"}}}}}]}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"404":{"$ref":"#/components/responses/NotFoundError"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/v1/ai/media-config":{"post":{"summary":"Generate a legacy media configuration","tags":["AI","userApi"],"security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"Configuration generated"}}}},"/api/v1/ai/usage":{"get":{"summary":"Get legacy AI usage","tags":["AI","userApi"],"security":[{"BearerAuth":[]}],"responses":{"200":{"description":"AI usage returned"}}}}}}