openapi: 3.1.0
info:
  title: AIStemSplitter Public API
  version: 0.1.0
  description: Public API for asynchronous AI audio stem splitting.
servers:
  - url: https://api.aistemsplitter.org/v1
security:
  - bearerAuth: []
paths:
  /audio/uploads:
    post:
      summary: Create an audio upload reservation
      operationId: createAudioUpload
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAudioUploadRequest'
      responses:
        '200':
          description: Upload reservation created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateAudioUploadResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/ValidationError'
  /audio/splits:
    post:
      summary: Create an audio split job
      operationId: createAudioSplit
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAudioSplitRequest'
      responses:
        '200':
          description: Split job queued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateAudioSplitResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCredits'
        '429':
          $ref: '#/components/responses/RateLimited'
        '422':
          $ref: '#/components/responses/ValidationError'
  /audio/splits/{splitId}:
    get:
      summary: Get an audio split job
      operationId: getAudioSplit
      parameters:
        - name: splitId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Split job status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetAudioSplitResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /credits:
    get:
      summary: Get current credit balance
      operationId: getCredits
      responses:
        '200':
          description: Credit balance
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetCreditsResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: AIStemSplitter API key
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      schema:
        type: string
        maxLength: 128
  responses:
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    InsufficientCredits:
      description: The account does not have enough credits
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    ValidationError:
      description: Request validation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    RateLimited:
      description: The API key exceeded its per-minute request limit
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  schemas:
    ErrorResponse:
      type: object
      required: [success, error]
      properties:
        success:
          type: boolean
          const: false
        error:
          type: object
          required: [code, message]
          properties:
            code:
              type: string
            message:
              type: string
    CreateAudioUploadRequest:
      type: object
      required: [filename, contentType, contentLength]
      properties:
        filename:
          type: string
        contentType:
          type: string
          enum:
            - audio/mpeg
            - audio/wav
            - audio/flac
            - audio/mp4
            - audio/x-m4a
            - audio/x-wav
            - audio/webm
        contentLength:
          type: integer
          minimum: 1
          maximum: 52428800
    CreateAudioUploadResponse:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          type: object
          required: [uploadId, uploadUrl, uploadHeaders, expiresAt]
          properties:
            uploadId:
              type: string
            uploadUrl:
              type: string
              format: uri
            uploadHeaders:
              type: object
              additionalProperties:
                type: string
            expiresAt:
              type: string
              format: date-time
    CreateAudioSplitRequest:
      type: object
      required: [input]
      properties:
        input:
          oneOf:
            - $ref: '#/components/schemas/DirectUrlInput'
            - $ref: '#/components/schemas/UploadedFileInput'
          discriminator:
            propertyName: type
        stemModel:
          type: string
          enum: ['4s', '6s']
          default: '6s'
        outputFormat:
          type: string
          enum: [mp3]
          default: mp3
        webhookUrl:
          type: string
          format: uri
    DirectUrlInput:
      type: object
      required: [type, url]
      properties:
        type:
          type: string
          const: direct_url
        url:
          type: string
          format: uri
    UploadedFileInput:
      type: object
      required: [type, uploadId, fileUrl, storageKey]
      properties:
        type:
          type: string
          const: uploaded_file
        uploadId:
          type: string
        fileUrl:
          type: string
          format: uri
        storageKey:
          type: string
    CreateAudioSplitResponse:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          type: object
          required: [id, status, creditsUsed, createdAt]
          properties:
            id:
              type: string
            status:
              type: string
              const: queued
            creditsUsed:
              type: integer
            createdAt:
              type: string
              format: date-time
    GetAudioSplitResponse:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          $ref: '#/components/schemas/AudioSplit'
    AudioSplit:
      type: object
      required:
        - id
        - status
        - stemModel
        - filename
        - durationSeconds
        - creditsUsed
        - createdAt
        - updatedAt
        - error
      properties:
        id:
          type: string
        status:
          type: string
          enum: [queued, processing, succeeded, failed]
        stemModel:
          type: string
          enum: ['4s', '6s']
        filename:
          type: string
        durationSeconds:
          type: integer
        creditsUsed:
          type: integer
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        stems:
          type: object
          additionalProperties:
            type: string
            format: uri
        error:
          oneOf:
            - type: 'null'
            - type: object
              required: [code, message]
              properties:
                code:
                  type: string
                message:
                  type: string
    GetCreditsResponse:
      type: object
      required: [success, data]
      properties:
        success:
          type: boolean
          const: true
        data:
          type: object
          required: [balance, unit]
          properties:
            balance:
              type: integer
            unit:
              type: string
              const: seconds
