{
  "openapi": "3.1.0",
  "info": {
    "title": "Opus Video Maker API",
    "version": "1.0.0",
    "description": "Turns a brief, plus optional images, audio, video, PDFs or links, into a finished MP4: an AI director plans the shots, writes the animation as code, renders it frame by frame and adds voice-over. A video takes about 10-60 minutes and is billed to the account balance.\n\nA video takes 10-60 minutes: create returns at once, then poll /wait or use callback_url. Guide: https://maker.vcorp.ai/docs/agents.md"
  },
  "servers": [
    {
      "url": "https://maker.vcorp.ai/v1"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "externalDocs": {
    "url": "https://maker.vcorp.ai/docs/agents.md"
  },
  "paths": {
    "/account": {
      "get": {
        "operationId": "getAccount",
        "summary": "Balance, limits and active jobs",
        "responses": {
          "200": {
            "description": "Account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/videos": {
      "post": {
        "operationId": "createVideo",
        "summary": "Start a video (returns at once)",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "description": "Same as client_request_id: a repeat within 24 h returns the existing job."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateVideo"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Job already created by an earlier request with this key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Video"
                }
              }
            }
          },
          "202": {
            "description": "Job created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Video"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "402": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "get": {
        "operationId": "listVideos",
        "summary": "Recent jobs, newest first",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Jobs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobs": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/videos/{id}": {
      "get": {
        "operationId": "getVideo",
        "summary": "State, progress and download links",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "job_id"
          }
        ],
        "responses": {
          "200": {
            "description": "Job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Video"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/videos/{id}/wait": {
      "get": {
        "operationId": "waitForVideo",
        "summary": "Long poll: returns when the job stops running, or after timeout (max 55s)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "job_id"
          },
          {
            "name": "timeout",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 55,
              "default": 45
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Video"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/videos/{id}/approve": {
      "post": {
        "operationId": "approvePlan",
        "summary": "Accept the plan and render",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "job_id"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "feedback": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Video"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "402": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/videos/{id}/revise": {
      "post": {
        "operationId": "reviseVideo",
        "summary": "Redo the plan, or re-render a finished video, with feedback",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "job_id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "feedback": {
                    "type": "string"
                  }
                },
                "required": [
                  "feedback"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Video"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "402": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/videos/{id}/continue": {
      "post": {
        "operationId": "continueVideo",
        "summary": "Resume a failed, interrupted or cancelled job",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "job_id"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "note": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Video"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "402": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/videos/{id}/cancel": {
      "post": {
        "operationId": "cancelVideo",
        "summary": "Stop a queued or running job",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "job_id"
          }
        ],
        "responses": {
          "200": {
            "description": "Job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Video"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key (ovm_...) from the web app, 接入 Agent."
      }
    },
    "schemas": {
      "CreateVideo": {
        "type": "object",
        "properties": {
          "prompt": {
            "type": "string",
            "description": "The brief: what the video is about, audience, tone, must-have shots or text, and how to use the attached material. Any language."
          },
          "aspect": {
            "type": "string",
            "enum": [
              "16:9",
              "9:16",
              "1:1",
              "4:3"
            ],
            "default": "16:9",
            "description": "Frame shape. 9:16 for vertical phone video."
          },
          "duration_seconds": {
            "type": "integer",
            "minimum": 3,
            "maximum": 600,
            "default": 30
          },
          "style": {
            "type": "string",
            "description": "Kind of video. auto: 由你按内容判断; explainer: 知识解说／科普动画; news: 竖屏资讯／评测; product: 产品宣传片; art: 艺术风格动画（按需求里描述的画风）; motion: 抽象动效／动态字体／showreel; mv: 跟音乐卡点的 MV; edit: 在用户的视频素材上剪辑、加字幕",
            "enum": [
              "auto",
              "explainer",
              "news",
              "product",
              "art",
              "motion",
              "mv",
              "edit"
            ],
            "default": "auto"
          },
          "voice": {
            "type": "string",
            "enum": [
              "none",
              "中文",
              "英文",
              "中英双语"
            ],
            "default": "中文",
            "description": "Voice-over: none, Chinese (中文), English (英文), or one version in each (中英双语)."
          },
          "budget_usd": {
            "type": "number",
            "minimum": 1,
            "maximum": 200,
            "default": 20,
            "description": "Hard cap on what this job may spend, in USD."
          },
          "effort": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high",
              "xhigh",
              "max"
            ],
            "default": "high",
            "description": "How hard the director thinks. Higher is slower and costs more."
          },
          "review": {
            "type": "boolean",
            "default": false,
            "description": "Stop after planning for approve_plan / revise_video instead of rendering straight away."
          },
          "links": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 10,
            "description": "http(s) links to articles, pages or PDFs to use as material. Fetched inside the sandbox."
          },
          "files": {
            "type": "array",
            "maxItems": 30,
            "description": "Small attachments inline, at most 20 MB decoded in total: images, audio, video, PDF, txt, md. Put larger material behind links.",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "File name with extension, e.g. screenshot.png"
                },
                "data_base64": {
                  "type": "string"
                }
              },
              "required": [
                "name",
                "data_base64"
              ]
            }
          },
          "client_request_id": {
            "type": "string",
            "maxLength": 100,
            "description": "Your own id for this request. Retrying with the same id within 24 hours returns the existing job instead of starting another."
          },
          "callback_url": {
            "type": "string",
            "maxLength": 2000,
            "description": "Optional https URL on a public host to POST to when the job settles (Standard Webhooks; the signing secret comes back as webhook.secret). Lets you stop polling."
          }
        },
        "required": [
          "prompt"
        ]
      },
      "Video": {
        "type": "object",
        "description": "A job as an agent sees it. Same shape as the MCP tool results.",
        "properties": {
          "job_id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "new",
              "queued",
              "planning",
              "awaiting_approval",
              "rendering",
              "done",
              "failed",
              "cancelled",
              "interrupted"
            ]
          },
          "status_text": {
            "type": "string"
          },
          "phase": {
            "type": [
              "string",
              "null"
            ]
          },
          "queue_position": {
            "type": "integer",
            "description": "Jobs of this account that run before this one."
          },
          "review": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "cost_usd": {
            "type": "number"
          },
          "budget_usd": {
            "type": "number"
          },
          "work_minutes": {
            "type": "number"
          },
          "elapsed_minutes": {
            "type": "number"
          },
          "video_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "The MP4, once there is one. Signed; expires at links_expire_at."
          },
          "contact_sheet_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "files": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "size_bytes": {
                  "type": "integer"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "links_expire_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "recent": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "at": {
                  "type": "string"
                },
                "kind": {
                  "type": "string"
                },
                "text": {
                  "type": "string"
                }
              }
            }
          },
          "next": {
            "type": "string",
            "description": "What to do now, in words."
          },
          "poll_after_seconds": {
            "type": [
              "integer",
              "null"
            ]
          },
          "web_url": {
            "type": "string",
            "format": "uri"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "notes": {
            "type": "string"
          },
          "duplicate": {
            "type": "boolean",
            "description": "True when an earlier request with the same client_request_id / Idempotency-Key created this job."
          },
          "webhook": {
            "type": "object",
            "description": "Present when callback_url was given. secret appears only in the create answer."
          }
        }
      },
      "Account": {
        "type": "object",
        "properties": {
          "username": {
            "type": "string"
          },
          "balance_usd": {
            "type": "number"
          },
          "available_usd": {
            "type": "number",
            "description": "Balance minus what running jobs have spent but not yet been billed."
          },
          "min_start_usd": {
            "type": "number"
          },
          "can_start": {
            "type": "boolean"
          },
          "active_jobs": {
            "type": "integer"
          },
          "max_active_jobs": {
            "type": "integer"
          },
          "limits": {
            "type": "object"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "status": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}