{"openapi":"3.1.0","info":{"title":"StemSplit API","description":"Public API for StemSplit - AI-powered stem separation for audio files. Separate vocals, drums, bass, and more from any song.","version":"1.0.0","contact":{"name":"StemSplit Support","url":"https://stemsplit.io/contact"}},"servers":[{"url":"https://stemsplit.io/api/v1","description":"Production"}],"security":[{"bearerAuth":[]}],"paths":{"/jobs":{"post":{"operationId":"createJob","summary":"Create a stem separation job","description":"Create a new stem separation job from an uploaded file or remote URL.","tags":["Jobs"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateJobRequest"},"examples":{"fromUpload":{"summary":"From presigned upload","value":{"uploadKey":"uploads/api_xxx/input.mp3","outputType":"BOTH","quality":"BEST","outputFormat":"MP3"}},"fromUrl":{"summary":"From remote URL","value":{"sourceUrl":"https://example.com/song.mp3","outputType":"FOUR_STEMS","quality":"BEST"}}}}}},"responses":{"201":{"description":"Job created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobCreatedResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InsufficientCreditsError"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"listJobs","summary":"List jobs","description":"List stem separation jobs for the authenticated user.","tags":["Jobs"],"parameters":[{"name":"limit","in":"query","description":"Maximum number of jobs to return (max 100)","schema":{"type":"integer","default":20,"maximum":100}},{"name":"offset","in":"query","description":"Number of jobs to skip for pagination","schema":{"type":"integer","default":0}},{"name":"status","in":"query","description":"Filter by job status","schema":{"type":"string","enum":["PENDING","PROCESSING","COMPLETED","FAILED","EXPIRED"]}}],"responses":{"200":{"description":"Jobs list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobsListResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/jobs/{id}":{"get":{"operationId":"getJob","summary":"Get job details","description":"Get the status and download links for a specific job.","tags":["Jobs"],"parameters":[{"name":"id","in":"path","required":true,"description":"Job ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Job details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobDetailResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/upload":{"post":{"operationId":"getUploadUrl","summary":"Get presigned upload URL","description":"Get a presigned URL for uploading an audio file. Use the returned uploadKey when creating a job.","tags":["Upload"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadRequest"},"example":{"filename":"my-song.mp3"}}}},"responses":{"200":{"description":"Presigned upload URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/balance":{"get":{"operationId":"getBalance","summary":"Get credit balance","description":"Get the current credit balance for the authenticated user.","tags":["Account"],"responses":{"200":{"description":"Credit balance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BalanceResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/webhooks":{"post":{"operationId":"createWebhook","summary":"Create a webhook","description":"Register a webhook URL to receive job completion notifications.","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookRequest"},"example":{"url":"https://your-server.com/webhook","events":["job.completed","job.failed"]}}}},"responses":{"201":{"description":"Webhook created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreatedResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"listWebhooks","summary":"List webhooks","description":"List all registered webhooks for the authenticated user.","tags":["Webhooks"],"responses":{"200":{"description":"Webhooks list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhooksListResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/webhooks/{id}":{"delete":{"operationId":"deleteWebhook","summary":"Delete a webhook","description":"Delete a registered webhook.","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"description":"Webhook ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Webhook deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","example":true}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Webhook not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key authentication. Get your API key from Settings > API Keys in your dashboard."}},"responses":{"Unauthorized":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"MISSING_API_KEY","message":"Missing API key. Include 'Authorization: Bearer sk_live_xxx' header."}}}}},"RateLimited":{"description":"Rate limit exceeded","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per minute"},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests remaining in current window"},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix timestamp when rate limit resets"},"Retry-After":{"schema":{"type":"integer"},"description":"Seconds until rate limit resets"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":{"code":"RATE_LIMIT_EXCEEDED","message":"Rate limit exceeded. Try again in 45 seconds."}}}}}},"schemas":{"ErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Error code for programmatic handling"},"message":{"type":"string","description":"Human-readable error message"}},"required":["code","message"]}}},"InsufficientCreditsError":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"INSUFFICIENT_CREDITS"},"message":{"type":"string","example":"Not enough credits for this job."},"requiredSeconds":{"type":"integer","description":"Credits required for this job (in seconds)"},"purchaseUrl":{"type":"string","description":"URL to purchase more credits"}}}}},"CreateJobRequest":{"type":"object","properties":{"uploadKey":{"type":"string","description":"Key from POST /upload. Use this OR sourceUrl."},"sourceUrl":{"type":"string","description":"URL of audio file to process. Use this OR uploadKey."},"fileName":{"type":"string","description":"Original filename to store with the job (e.g. 'my-song.wav'). Overrides the default derived name. Useful when uploading via presigned URL where the original name is not preserved."},"outputType":{"type":"string","enum":["VOCALS","INSTRUMENTAL","BOTH","FOUR_STEMS","SIX_STEMS"],"default":"BOTH","description":"What stems to separate"},"quality":{"type":"string","enum":["FAST","BALANCED","BEST"],"default":"BEST","description":"Processing quality"},"outputFormat":{"type":"string","enum":["WAV","MP3","FLAC"],"default":"MP3","description":"Output file format"},"metadata":{"type":"object","description":"Custom metadata (returned in job response and webhooks)"}}},"JobCreatedResponse":{"type":"object","properties":{"id":{"type":"string","description":"Job ID"},"status":{"type":"string","enum":["PENDING","PROCESSING","COMPLETED","FAILED","EXPIRED"]},"progress":{"type":"integer","description":"Progress percentage (0-100)"},"createdAt":{"type":"string","format":"date-time"},"estimatedSeconds":{"type":"integer","description":"Estimated processing time in seconds"},"creditsRequired":{"type":"integer","description":"Credits that will be charged (in seconds)"},"input":{"type":"object","properties":{"fileName":{"type":"string"},"durationSeconds":{"type":"integer"},"fileSizeBytes":{"type":"integer"}}},"options":{"type":"object","properties":{"outputType":{"type":"string"},"quality":{"type":"string"},"outputFormat":{"type":"string"}}},"outputs":{"type":"object","nullable":true},"metadata":{"type":"object","nullable":true}}},"JobDetailResponse":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["PENDING","PROCESSING","COMPLETED","FAILED","EXPIRED"]},"progress":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"},"startedAt":{"type":"string","format":"date-time","nullable":true},"completedAt":{"type":"string","format":"date-time","nullable":true},"input":{"type":"object","properties":{"fileName":{"type":"string"},"durationSeconds":{"type":"integer"},"fileSizeBytes":{"type":"integer"}}},"options":{"type":"object","properties":{"outputType":{"type":"string"},"quality":{"type":"string"},"outputFormat":{"type":"string"}}},"outputs":{"type":"object","nullable":true,"description":"Download URLs for completed jobs","additionalProperties":{"type":"object","properties":{"url":{"type":"string","description":"Presigned download URL"},"expiresAt":{"type":"string","format":"date-time"}}}},"creditsCharged":{"type":"integer"},"errorMessage":{"type":"string","nullable":true},"expiresAt":{"type":"string","format":"date-time","nullable":true}}},"JobsListResponse":{"type":"object","properties":{"jobs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"progress":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time","nullable":true},"input":{"type":"object","properties":{"fileName":{"type":"string"},"durationSeconds":{"type":"integer"}}},"options":{"type":"object","properties":{"outputType":{"type":"string"},"quality":{"type":"string"},"outputFormat":{"type":"string"}}},"creditsCharged":{"type":"integer"},"errorMessage":{"type":"string","nullable":true}}}},"pagination":{"type":"object","properties":{"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"},"hasMore":{"type":"boolean"}}}}},"UploadRequest":{"type":"object","required":["filename"],"properties":{"filename":{"type":"string","description":"Name of the file to upload (e.g., 'song.mp3')"},"contentType":{"type":"string","description":"MIME type (auto-detected from filename if not provided)"}}},"UploadResponse":{"type":"object","properties":{"uploadUrl":{"type":"string","description":"Presigned URL - PUT your file here"},"uploadKey":{"type":"string","description":"Use this key when creating a job"},"expiresAt":{"type":"string","format":"date-time","description":"URL expiration time (15 minutes)"},"maxFileSizeBytes":{"type":"integer"},"maxFileSizeMb":{"type":"integer"},"contentType":{"type":"string"},"instructions":{"type":"object","properties":{"step1":{"type":"string"},"step2":{"type":"string"},"note":{"type":"string"}}}}},"BalanceResponse":{"type":"object","properties":{"balanceSeconds":{"type":"integer","description":"Balance in seconds"},"balanceMinutes":{"type":"integer","description":"Balance in whole minutes"},"balanceFormatted":{"type":"string","description":"Human-readable balance"},"updatedAt":{"type":"string","format":"date-time"}}},"CreateWebhookRequest":{"type":"object","required":["url"],"properties":{"url":{"type":"string","description":"URL to receive webhook notifications"},"events":{"type":"array","items":{"type":"string","enum":["job.completed","job.failed"]},"default":["job.completed","job.failed"],"description":"Events to subscribe to"}}},"WebhookCreatedResponse":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"secret":{"type":"string","description":"Signing secret - SAVE THIS, it's only shown once"},"isActive":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}}},"WebhooksListResponse":{"type":"object","properties":{"webhooks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean"},"failCount":{"type":"integer"},"lastError":{"type":"string","nullable":true},"lastCalledAt":{"type":"string","format":"date-time","nullable":true},"createdAt":{"type":"string","format":"date-time"}}}}}},"WebhookPayload":{"type":"object","description":"Payload sent to webhook endpoints","properties":{"event":{"type":"string","enum":["job.completed","job.failed"]},"timestamp":{"type":"string","format":"date-time"},"data":{"type":"object","properties":{"jobId":{"type":"string"},"status":{"type":"string"},"input":{"type":"object"},"options":{"type":"object"},"outputs":{"type":"object","nullable":true},"creditsCharged":{"type":"integer"},"errorMessage":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time","nullable":true}}}}}}}}