{
    "openapi": "3.1.0",
    "info": {
        "title": "Vyexa API",
        "version": "1.0.0",
        "summary": "Turn a video link into vertical short clips with burned-in subtitles.",
        "description": "Send a video URL (YouTube, TikTok, Instagram, Vimeo, Twitch or a direct file link), poll the job, download the clips. The free plan works through the API on the same terms as the website. Also available as a remote MCP server at https://vyexa.net/mcp. Human docs: https://vyexa.net/api-docs.",
        "contact": {
            "name": "Vyexa support",
            "email": "support@vyexa.net"
        }
    },
    "servers": [
        {
            "url": "https://vyexa.net"
        }
    ],
    "externalDocs": {
        "description": "Full documentation",
        "url": "https://vyexa.net/api-docs"
    },
    "security": [
        {
            "bearerAuth": []
        }
    ],
    "tags": [
        {
            "name": "Jobs"
        },
        {
            "name": "Clips"
        },
        {
            "name": "Discovery"
        }
    ],
    "paths": {
        "/api/v1/options": {
            "get": {
                "tags": [
                    "Discovery"
                ],
                "operationId": "getOptions",
                "summary": "List accepted styling values",
                "description": "All accepted values of constructor.layout / subtitle_style / title_style / font / subtitle_position with \"when to choose\" hints and recommendations by content type.",
                "responses": {
                    "200": {
                        "description": "Options catalog",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Error"
                    }
                }
            }
        },
        "/api/v1/jobs": {
            "post": {
                "tags": [
                    "Jobs"
                ],
                "operationId": "createJob",
                "summary": "Create a clipping job",
                "description": "Queues a job. Poll the returned status_url. num_clips is a maximum; balance is charged only for clips actually rendered.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/CreateJob"
                            }
                        }
                    }
                },
                "responses": {
                    "202": {
                        "description": "Accepted",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/JobCreated"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Error"
                    },
                    "403": {
                        "$ref": "#/components/responses/Error"
                    },
                    "422": {
                        "$ref": "#/components/responses/Error"
                    },
                    "429": {
                        "$ref": "#/components/responses/Error"
                    }
                }
            }
        },
        "/api/v1/jobs/{job_id}": {
            "get": {
                "tags": [
                    "Jobs"
                ],
                "operationId": "getJob",
                "summary": "Job status and finished clips",
                "description": "Poll every 10-15 seconds. status: pending → processing → completed | partial | failed. \"partial\" is a success state.",
                "parameters": [
                    {
                        "name": "job_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "include_subtitles",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "boolean"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Job status",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Job"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Error"
                    },
                    "404": {
                        "$ref": "#/components/responses/Error"
                    },
                    "429": {
                        "$ref": "#/components/responses/Error"
                    }
                }
            }
        },
        "/api/v1/clips/{clip_id}": {
            "get": {
                "tags": [
                    "Clips"
                ],
                "operationId": "getClip",
                "summary": "Clip status",
                "parameters": [
                    {
                        "name": "clip_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "pattern": "^[A-Z0-9]+$"
                        },
                        "description": "Public clip id from the job status."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Clip",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Clip"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Error"
                    },
                    "404": {
                        "$ref": "#/components/responses/Error"
                    }
                }
            }
        },
        "/api/v1/clips/{clip_id}/download": {
            "get": {
                "tags": [
                    "Clips"
                ],
                "operationId": "downloadClip",
                "summary": "Download the finished MP4",
                "description": "Requires the same Bearer key. Returns the video file.",
                "parameters": [
                    {
                        "name": "clip_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "pattern": "^[A-Z0-9]+$"
                        },
                        "description": "Public clip id from the job status."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "MP4 video",
                        "content": {
                            "video/mp4": {
                                "schema": {
                                    "type": "string",
                                    "format": "binary"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Error"
                    },
                    "404": {
                        "$ref": "#/components/responses/Error"
                    },
                    "409": {
                        "$ref": "#/components/responses/Error"
                    },
                    "429": {
                        "$ref": "#/components/responses/Error"
                    }
                }
            }
        },
        "/api/v1/clips/{clip_id}/edit": {
            "post": {
                "tags": [
                    "Clips"
                ],
                "operationId": "editClip",
                "summary": "Change title or trim a finished clip",
                "parameters": [
                    {
                        "name": "clip_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "pattern": "^[A-Z0-9]+$"
                        },
                        "description": "Public clip id from the job status."
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "minProperties": 1,
                                "properties": {
                                    "title": {
                                        "type": "string",
                                        "maxLength": 32
                                    },
                                    "trim": {
                                        "type": "object",
                                        "required": [
                                            "start",
                                            "end"
                                        ],
                                        "properties": {
                                            "start": {
                                                "type": "number"
                                            },
                                            "end": {
                                                "type": "number"
                                            }
                                        },
                                        "description": "Seconds, relative to the clip."
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Edit accepted",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Error"
                    },
                    "404": {
                        "$ref": "#/components/responses/Error"
                    },
                    "409": {
                        "$ref": "#/components/responses/Error"
                    },
                    "422": {
                        "$ref": "#/components/responses/Error"
                    }
                }
            }
        }
    },
    "components": {
        "securitySchemes": {
            "bearerAuth": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "vx_<48 hex>",
                "description": "API key from https://vyexa.net/content/dashboard/api-keys (free account)."
            }
        },
        "responses": {
            "Error": {
                "description": "Error with a stable machine-readable error_code",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        }
                    }
                }
            }
        },
        "schemas": {
            "Error": {
                "type": "object",
                "required": [
                    "success",
                    "error_code",
                    "error"
                ],
                "properties": {
                    "success": {
                        "const": false
                    },
                    "error_code": {
                        "type": "string",
                        "examples": [
                            "unauthorized",
                            "rate_limit",
                            "insufficient_balance",
                            "concurrent_limit",
                            "invalid_clip_duration",
                            "clip_duration_not_in_plan"
                        ]
                    },
                    "error": {
                        "type": "string"
                    },
                    "retry_after_seconds": {
                        "type": "integer"
                    }
                }
            },
            "CreateJob": {
                "type": "object",
                "required": [
                    "url"
                ],
                "properties": {
                    "url": {
                        "type": "string",
                        "format": "uri",
                        "description": "Video post link or direct https file link."
                    },
                    "language": {
                        "type": "string",
                        "description": "Source language code or \"auto\"."
                    },
                    "num_clips": {
                        "type": "integer",
                        "minimum": 1,
                        "description": "Maximum clips to produce."
                    },
                    "segment_duration": {
                        "type": "integer",
                        "enum": [
                            30,
                            50,
                            90
                        ],
                        "description": "50 needs Creator, 90 needs Pro."
                    },
                    "generate_title": {
                        "type": "boolean",
                        "default": true
                    },
                    "title": {
                        "type": "string",
                        "maxLength": 32
                    },
                    "highlight_color": {
                        "type": "string",
                        "description": "Catalog id or #RRGGBB (or #RRGGBB+#RRGGBB)."
                    },
                    "profanity_censor": {
                        "type": [
                            "boolean",
                            "null"
                        ]
                    },
                    "constructor": {
                        "type": "object",
                        "description": "Look of the clips. Valid values: GET /api/v1/options.",
                        "properties": {
                            "layout": {
                                "type": "string",
                                "examples": [
                                    "auto",
                                    "full",
                                    "frame_70",
                                    "frame_50",
                                    "frame_40",
                                    "dual",
                                    "streaming",
                                    "lesson"
                                ]
                            },
                            "subtitle_style": {
                                "type": "string",
                                "examples": [
                                    "default",
                                    "karaoke",
                                    "highlighter",
                                    "glow",
                                    "punch",
                                    "none"
                                ]
                            },
                            "title_style": {
                                "type": "string",
                                "examples": [
                                    "clean",
                                    "box",
                                    "chip",
                                    "outline",
                                    "off"
                                ]
                            },
                            "font": {
                                "type": "string",
                                "examples": [
                                    "montserrat",
                                    "rubik",
                                    "russo"
                                ]
                            },
                            "subtitle_position": {
                                "type": "string",
                                "enum": [
                                    "auto",
                                    "bottom",
                                    "middle",
                                    "top"
                                ]
                            }
                        }
                    },
                    "logo": {
                        "type": "object",
                        "description": "Paid plans only. See https://vyexa.net/api-docs#logo."
                    }
                }
            },
            "JobCreated": {
                "type": "object",
                "properties": {
                    "success": {
                        "const": true
                    },
                    "job_id": {
                        "type": "integer"
                    },
                    "status": {
                        "const": "pending"
                    },
                    "status_url": {
                        "type": "string",
                        "format": "uri"
                    }
                }
            },
            "Job": {
                "type": "object",
                "properties": {
                    "success": {
                        "type": "boolean"
                    },
                    "job_id": {
                        "type": "integer"
                    },
                    "status": {
                        "type": "string",
                        "enum": [
                            "pending",
                            "processing",
                            "completed",
                            "partial",
                            "failed"
                        ]
                    },
                    "stage": {
                        "type": "string"
                    },
                    "clips_expected": {
                        "type": "integer"
                    },
                    "clips_ready": {
                        "type": "integer"
                    },
                    "clips_failed": {
                        "type": "integer"
                    },
                    "clips": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/ClipSummary"
                        }
                    },
                    "source": {
                        "type": "object"
                    },
                    "error_code": {
                        "type": "string"
                    },
                    "error": {
                        "type": "string"
                    }
                }
            },
            "ClipSummary": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string"
                    },
                    "title": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "duration": {
                        "type": [
                            "number",
                            "null"
                        ]
                    },
                    "download_url": {
                        "type": "string",
                        "format": "uri"
                    }
                }
            },
            "Clip": {
                "type": "object",
                "properties": {
                    "success": {
                        "type": "boolean"
                    },
                    "clip_id": {
                        "type": "string"
                    },
                    "status": {
                        "type": "string",
                        "enum": [
                            "processing",
                            "ready",
                            "failed"
                        ]
                    },
                    "title": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "duration": {
                        "type": [
                            "number",
                            "null"
                        ]
                    },
                    "download_url": {
                        "type": "string",
                        "format": "uri"
                    }
                }
            }
        }
    }
}