{
  "openapi": "3.1.0",
  "info": {
    "title": "Sonilo REST API",
    "version": "1.0.0",
    "description": "Sonilo API for licensed AI music, video-to-music soundtracks, sound effects, audio ducking, account usage, and async task polling.",
    "contact": {
      "name": "Sonilo",
      "url": "https://sonilo.com/contact-sales",
      "email": "info@sonilo.com"
    }
  },
  "externalDocs": {
    "description": "Sonilo API documentation",
    "url": "https://platform.sonilo.com/docs"
  },
  "servers": [
    {
      "url": "https://api.sonilo.com",
      "description": "Production API"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Workstation",
      "description": "Account services and usage"
    },
    {
      "name": "Music",
      "description": "Music generation and audio ducking"
    },
    {
      "name": "Sound Effects",
      "description": "Sound effect generation"
    },
    {
      "name": "Async Tasks",
      "description": "Task polling for asynchronous jobs"
    }
  ],
  "paths": {
    "/v1/account/services": {
      "get": {
        "operationId": "listAccountServices",
        "summary": "List available account services",
        "description": "Returns the Sonilo services and API capabilities enabled for the authenticated account.",
        "tags": ["Workstation"],
        "responses": {
          "200": {
            "description": "Available account services",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServicesResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/account/usage": {
      "get": {
        "operationId": "getAccountUsage",
        "summary": "Get account usage",
        "description": "Returns current account usage and remaining API credits for the authenticated account.",
        "tags": ["Workstation"],
        "responses": {
          "200": {
            "description": "Account usage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/text-to-music": {
      "post": {
        "operationId": "createMusicFromText",
        "summary": "Generate music from a text prompt",
        "description": "Creates licensed music from a text prompt. Use mode=async for long jobs, then poll /v1/tasks/{task_id}.",
        "tags": ["Music"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TextPromptRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TextPromptRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/GenerationAccepted"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/video-to-music": {
      "post": {
        "operationId": "createMusicFromVideo",
        "summary": "Generate a soundtrack from a video",
        "description": "Creates a licensed soundtrack aligned to a video. Use this endpoint for music beds, not isolated sound effects. Use mode=async for long jobs, then poll /v1/tasks/{task_id}.",
        "tags": ["Music"],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/VideoInputRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VideoUrlRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/GenerationAccepted"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/audio-ducking": {
      "post": {
        "operationId": "createAudioDuckingMix",
        "summary": "Apply audio ducking",
        "description": "Mixes foreground speech or dialogue with background music while lowering music volume around speech.",
        "tags": ["Music"],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/AudioDuckingRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AudioDuckingUrlRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/GenerationAccepted"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/text-to-sfx": {
      "post": {
        "operationId": "createSoundEffectsFromText",
        "summary": "Generate sound effects from a text prompt",
        "description": "Creates isolated sound effects from a text prompt. Use this endpoint for SFX, Foley, impacts, ambience, UI sounds, and transitions.",
        "tags": ["Sound Effects"],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TextPromptRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TextPromptRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/GenerationAccepted"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/video-to-sfx": {
      "post": {
        "operationId": "createSoundEffectsFromVideo",
        "summary": "Generate sound effects from a video",
        "description": "Creates sound effects aligned to visual moments in a video. Use this endpoint for SFX, not background music. Use mode=async for longer jobs, then poll /v1/tasks/{task_id}.",
        "tags": ["Sound Effects"],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/VideoInputRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VideoUrlRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/GenerationAccepted"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/tasks/{task_id}": {
      "get": {
        "operationId": "getTask",
        "summary": "Get async task status",
        "description": "Polls an asynchronous generation task until it reaches completed or failed. The task result contains the generated asset URL when available.",
        "tags": ["Async Tasks"],
        "parameters": [
          {
            "name": "task_id",
            "in": "path",
            "required": true,
            "description": "The task id returned by an async generation endpoint.",
            "schema": {
              "type": "string",
              "examples": ["task_123"]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Task status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Use your Sonilo API key as a bearer token."
      }
    },
    "responses": {
      "GenerationAccepted": {
        "description": "Generated audio result or an async task",
        "content": {
          "application/json": {
            "schema": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/GenerationResponse"
                },
                {
                  "$ref": "#/components/schemas/TaskCreatedResponse"
                }
              ]
            }
          }
        }
      },
      "BadRequest": {
        "description": "Invalid request payload or unsupported media input"
      },
      "Unauthorized": {
        "description": "Missing or invalid API key"
      },
      "NotFound": {
        "description": "Requested resource was not found"
      }
    },
    "schemas": {
      "ServicesResponse": {
        "type": "object",
        "properties": {
          "services": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "capabilities": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "UsageResponse": {
        "type": "object",
        "properties": {
          "credits_remaining": {
            "type": "number"
          },
          "credits_used": {
            "type": "number"
          },
          "period_start": {
            "type": "string",
            "format": "date-time"
          },
          "period_end": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "TextPromptRequest": {
        "type": "object",
        "required": ["prompt"],
        "properties": {
          "prompt": {
            "type": "string",
            "description": "Music or sound effect prompt."
          },
          "duration": {
            "type": "number",
            "description": "Desired output duration in seconds."
          },
          "mode": {
            "$ref": "#/components/schemas/GenerationMode"
          },
          "output_format": {
            "$ref": "#/components/schemas/AudioFormat"
          }
        },
        "additionalProperties": true
      },
      "VideoInputRequest": {
        "type": "object",
        "properties": {
          "video": {
            "type": "string",
            "format": "binary",
            "description": "Video file to analyze."
          },
          "prompt": {
            "type": "string",
            "description": "Optional creative direction."
          },
          "segments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Segment"
            }
          },
          "mode": {
            "$ref": "#/components/schemas/GenerationMode"
          },
          "output_format": {
            "$ref": "#/components/schemas/AudioFormat"
          }
        },
        "additionalProperties": true
      },
      "VideoUrlRequest": {
        "type": "object",
        "required": ["video_url"],
        "properties": {
          "video_url": {
            "type": "string",
            "format": "uri",
            "description": "Public or signed URL for the source video."
          },
          "prompt": {
            "type": "string"
          },
          "segments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Segment"
            }
          },
          "mode": {
            "$ref": "#/components/schemas/GenerationMode"
          },
          "output_format": {
            "$ref": "#/components/schemas/AudioFormat"
          }
        },
        "additionalProperties": true
      },
      "AudioDuckingRequest": {
        "type": "object",
        "properties": {
          "voice": {
            "type": "string",
            "format": "binary",
            "description": "Foreground speech or dialogue audio."
          },
          "music": {
            "type": "string",
            "format": "binary",
            "description": "Background music audio."
          },
          "ducking_level": {
            "type": "number",
            "description": "Amount to lower background music during speech."
          },
          "mode": {
            "$ref": "#/components/schemas/GenerationMode"
          },
          "output_format": {
            "$ref": "#/components/schemas/AudioFormat"
          }
        },
        "additionalProperties": true
      },
      "AudioDuckingUrlRequest": {
        "type": "object",
        "properties": {
          "voice_url": {
            "type": "string",
            "format": "uri"
          },
          "music_url": {
            "type": "string",
            "format": "uri"
          },
          "ducking_level": {
            "type": "number"
          },
          "mode": {
            "$ref": "#/components/schemas/GenerationMode"
          },
          "output_format": {
            "$ref": "#/components/schemas/AudioFormat"
          }
        },
        "additionalProperties": true
      },
      "Segment": {
        "type": "object",
        "required": ["start", "end"],
        "properties": {
          "start": {
            "type": "number",
            "description": "Segment start time in seconds."
          },
          "end": {
            "type": "number",
            "description": "Segment end time in seconds."
          },
          "prompt": {
            "type": "string",
            "description": "Optional prompt for this segment."
          }
        },
        "additionalProperties": true
      },
      "GenerationMode": {
        "type": "string",
        "enum": ["sync", "async"],
        "default": "sync"
      },
      "AudioFormat": {
        "type": "string",
        "enum": ["wav", "mp3", "aac", "flac"],
        "default": "aac"
      },
      "GenerationResponse": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "examples": ["completed"]
          },
          "audio_url": {
            "type": "string",
            "format": "uri"
          },
          "download_url": {
            "type": "string",
            "format": "uri"
          },
          "duration": {
            "type": "number"
          }
        },
        "additionalProperties": true
      },
      "TaskCreatedResponse": {
        "type": "object",
        "required": ["task_id", "status"],
        "properties": {
          "task_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "examples": ["queued", "running"]
          },
          "poll_url": {
            "type": "string",
            "examples": ["/v1/tasks/task_123"]
          }
        },
        "additionalProperties": true
      },
      "Task": {
        "type": "object",
        "required": ["task_id", "status"],
        "properties": {
          "task_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": ["queued", "running", "completed", "failed", "canceled"]
          },
          "progress": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "result": {
            "type": "object",
            "additionalProperties": true
          },
          "error": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      }
    }
  }
}
