{
  "openapi": "3.1.0",
  "info": {
    "title": "Pictory API",
    "version": "1.0.0",
    "description": "The Pictory API enables programmatic creation, preview, and rendering of AI-generated videos. This specification covers the most-used endpoints for the storyboard, render, project, jobs, and branding workflows.\n\nAuthentication uses a direct API key (no Bearer prefix). Obtain a key from the Pictory App at https://app.pictory.ai/api-access — keys start with the prefix `pictai_`.",
    "contact": {
      "name": "Pictory API Support",
      "url": "https://app.pictory.ai/api-access"
    }
  },
  "servers": [
    {
      "url": "https://api.pictory.ai",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "tags": [
    { "name": "Videos", "description": "Storyboard preview, render, and project rendering endpoints" },
    { "name": "Jobs", "description": "Async job status endpoints" },
    { "name": "Branding", "description": "Video brand configuration endpoints" }
  ],
  "paths": {
    "/pictoryapis/v2/video/storyboard": {
      "post": {
        "tags": ["Videos"],
        "operationId": "createStoryboardPreview",
        "summary": "Create Storyboard Preview",
        "description": "Generates a preview of your video project so you can review storyboard structure, scene breakdown, and visual selections before committing to the final render.\nReturns a `jobId` which should be polled against the Get Storyboard Preview Job endpoint.\nUse this when you want to validate content before incurring full rendering cost.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/StoryboardRequest" },
              "example": {
                "videoName": "demo_text_to_video",
                "voiceOver": {
                  "enabled": true,
                  "aiVoices": [{ "speaker": "Brian", "speed": 100 }]
                },
                "scenes": [
                  {
                    "story": "AI is transforming how we create content. It automates tasks and saves time.",
                    "createSceneOnEndOfSentence": true
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Job successfully created",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/JobCreatedResponse" },
                "example": {
                  "success": true,
                  "data": { "jobId": "a1d36612-326d-4b81-aece-411f8aed4c70" }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ValidationError" },
                "example": {
                  "code": "INVALID_REQUEST_BODY",
                  "message": "Request body validation failed.",
                  "fields": [{ "name": "scenes", "errors": "scenes is required" }]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/UnauthorizedError" },
                "example": { "message": "Unauthorized" }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/InternalError" }
              }
            }
          }
        }
      }
    },
    "/pictoryapis/v2/video/storyboard/render": {
      "post": {
        "tags": ["Videos"],
        "operationId": "renderStoryboardVideo",
        "summary": "Render Storyboard Video",
        "description": "Renders the final video file directly from a storyboard configuration, skipping the preview step.\nAccepts the same request body as Create Storyboard Preview.\nReturns a `jobId` that can be polled at the Get Video Render Job endpoint.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/StoryboardRequest" },
              "example": {
                "videoName": "demo_render",
                "aspectRatio": "16:9",
                "voiceOver": {
                  "enabled": true,
                  "aiVoices": [{ "speaker": "Brian", "speed": 100 }]
                },
                "backgroundMusic": { "enabled": true, "autoMusic": true, "volume": 0.5 },
                "scenes": [
                  {
                    "story": "Welcome to our product showcase. Discover amazing features.",
                    "createSceneOnEndOfSentence": true
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Render job successfully created",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/JobCreatedResponse" },
                "example": {
                  "success": true,
                  "data": { "jobId": "265a7c1a-4985-4058-9208-68114f131a2b" }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ValidationError" },
                "example": {
                  "code": "INVALID_REQUEST_BODY",
                  "message": "Request body validation failed.",
                  "fields": [{ "name": "scenes", "errors": "scenes is required" }]
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/UnauthorizedError" },
                "example": { "message": "Unauthorized" }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/InternalError" } }
            }
          }
        }
      }
    },
    "/pictoryapis/v2/projects/{projectid}/render": {
      "post": {
        "tags": ["Videos"],
        "operationId": "renderProject",
        "summary": "Render Project",
        "description": "Initiates rendering for an existing Pictory project. Projects can be created via the Pictory App or saved via the Create Storyboard Preview API with `saveProject: true`.\nReturns a `jobId` that can be polled at the Get Video Render Job endpoint.",
        "parameters": [
          {
            "name": "projectid",
            "in": "path",
            "required": true,
            "description": "Unique identifier of the project to render. Obtain from the Get Projects API.",
            "schema": { "type": "string" },
            "example": "20251222191648030d7df02f5b4054d4ca8831f1369459e25"
          }
        ],
        "responses": {
          "200": {
            "description": "Render job successfully created",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/JobCreatedResponse" },
                "example": {
                  "success": true,
                  "data": { "jobId": "265a7c1a-4985-4058-9208-68114f131a2b" }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/UnauthorizedError" },
                "example": { "message": "Unauthorized" }
              }
            }
          },
          "404": {
            "description": "Project not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean", "example": false },
                    "message": { "type": "string", "example": "Project not found" }
                  }
                },
                "example": { "success": false, "message": "Project not found" }
              }
            }
          },
          "409": {
            "description": "Project is already being rendered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean", "example": false },
                    "message": { "type": "string" }
                  }
                },
                "example": { "success": false, "message": "Project is already being rendered" }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/InternalError" } }
            }
          }
        }
      }
    },
    "/pictoryapis/v1/jobs/{jobid}": {
      "get": {
        "tags": ["Jobs"],
        "operationId": "getVideoRenderJobById",
        "summary": "Get Video Render Job by ID",
        "description": "Retrieves the status and results of a video render job by its UUID.\nWhile a job is processing, the response contains progress information. Once completed, it returns all output URLs (video, audio, thumbnail, subtitle files).\nPoll this endpoint every 10-30 seconds, or use the `webhook` field on the render request for push notifications.",
        "parameters": [
          {
            "name": "jobid",
            "in": "path",
            "required": true,
            "description": "The unique UUID of the render job, returned by any render endpoint.",
            "schema": { "type": "string", "format": "uuid" },
            "example": "265a7c1a-4985-4058-9208-68114f131a2b"
          }
        ],
        "responses": {
          "200": {
            "description": "Job status retrieved. Inspect `data.status` for `in-progress`, `completed`, or `failed`. Note that `success: false` with `status: failed` is also returned with HTTP 200.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/JobStatusResponse" },
                "examples": {
                  "inProgress": {
                    "summary": "In progress (rendering)",
                    "value": {
                      "job_id": "265a7c1a-4985-4058-9208-68114f131a2b",
                      "success": true,
                      "data": {
                        "status": "in-progress",
                        "renderProgress": 2,
                        "renderProgressMessage": "Generating video",
                        "renderState": "RUNNING"
                      }
                    }
                  },
                  "completed": {
                    "summary": "Completed",
                    "value": {
                      "job_id": "265a7c1a-4985-4058-9208-68114f131a2b",
                      "success": true,
                      "data": {
                        "status": "completed",
                        "progress": 100,
                        "videoURL": "https://d3uryq9bhgb5qr.cloudfront.net/.../demo_text_to_video3.mp4",
                        "videoShareURL": "https://video.pictory.ai/.../20260326170050790Tkrsq44GvHibYw5",
                        "videoEmbedURL": "https://video.pictory.ai/embed/.../20260326170050790Tkrsq44GvHibYw5",
                        "audioURL": "https://d3uryq9bhgb5qr.cloudfront.net/.../demo_text_to_video3.mp3",
                        "thumbnail": "https://d3uryq9bhgb5qr.cloudfront.net/.../thumb.jpg",
                        "srtFile": "https://d3uryq9bhgb5qr.cloudfront.net/.../captions.srt",
                        "txtFile": "https://d3uryq9bhgb5qr.cloudfront.net/.../transcript.txt",
                        "vttFile": "https://d3uryq9bhgb5qr.cloudfront.net/.../captions.vtt",
                        "videoDuration": 65.6,
                        "encodingDuration": 117,
                        "aiCreditsUsed": 48
                      }
                    }
                  },
                  "failed": {
                    "summary": "Failed",
                    "value": {
                      "job_id": "265a7c1a-4985-4058-9208-68114f131a2b",
                      "success": false,
                      "data": {
                        "status": "failed",
                        "error_code": "TEXT_TO_VIDEO_FAILED",
                        "error_message": "The AI voice speaker [Timm] is invalid and is not supported. Please provide valid AI voice speaker."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/UnauthorizedError" },
                "example": { "message": "Unauthorized" }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/InternalError" } }
            }
          }
        }
      }
    },
    "/pictoryapis/v1/brands/video": {
      "get": {
        "tags": ["Branding"],
        "operationId": "getVideoBrands",
        "summary": "Get Video Brands",
        "description": "Returns the list of video brand configurations defined in the user's Pictory account.\nEach brand is identified by a UUID `id` (use this when applying a brand to a render) and a human-readable `name`.",
        "responses": {
          "200": {
            "description": "List of available video brands",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/VideoBrand" }
                },
                "example": [
                  { "id": "eb67b4ba-0eef-41fa-a772-d9675cc3645c", "name": "Default Brand" },
                  { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "name": "Corporate Blue" }
                ]
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/UnauthorizedError" },
                "example": { "message": "Unauthorized" }
              }
            }
          },
          "500": {
            "description": "Internal server error",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/InternalError" },
                "example": { "error": { "code": "INTERNAL_ERROR", "message": "An unexpected error occurred" } }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "Pictory API key. Pass the raw key value (which starts with `pictai_`) directly as the `Authorization` header value. Do NOT prefix with `Bearer`. Example: `Authorization: pictai_xxxxxxxxxxxxxxxx`. Obtain a key from https://app.pictory.ai/api-access."
      }
    },
    "schemas": {
      "JobCreatedResponse": {
        "type": "object",
        "properties": {
          "success": { "type": "boolean", "description": "Indicates whether the request was successful" },
          "data": {
            "type": "object",
            "properties": {
              "jobId": {
                "type": "string",
                "format": "uuid",
                "description": "Unique identifier for the created job. Use this with the Get Video Render Job by ID endpoint."
              }
            }
          }
        }
      },
      "ValidationError": {
        "type": "object",
        "properties": {
          "code": { "type": "string", "example": "INVALID_REQUEST_BODY" },
          "message": { "type": "string" },
          "fields": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": { "type": "string" },
                "errors": { "type": "string" }
              }
            }
          }
        }
      },
      "UnauthorizedError": {
        "type": "object",
        "properties": { "message": { "type": "string", "example": "Unauthorized" } }
      },
      "InternalError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": { "type": "string", "example": "INTERNAL_ERROR" },
              "message": { "type": "string" }
            }
          }
        }
      },
      "VideoBrand": {
        "type": "object",
        "required": ["id", "name"],
        "properties": {
          "id": { "type": "string", "format": "uuid", "description": "Unique identifier for the video brand" },
          "name": { "type": "string", "description": "Descriptive name of the video brand" }
        }
      },
      "JobStatusResponse": {
        "type": "object",
        "description": "Unified response shape for the Get Video Render Job by ID endpoint. The fields present in `data` vary by job status.",
        "properties": {
          "job_id": { "type": "string", "format": "uuid" },
          "id": { "type": "string", "description": "Alternate identifier present in the JOB_NOT_FOUND error response" },
          "success": { "type": "boolean" },
          "data": { "$ref": "#/components/schemas/JobStatusData" }
        }
      },
      "JobStatusData": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": ["in-progress", "completed", "failed"],
            "description": "Current job status"
          },
          "renderProgress": { "type": "number", "minimum": 0, "maximum": 100, "description": "Render progress percentage (0-100). Present once rendering has started." },
          "renderProgressMessage": { "type": "string", "description": "Descriptive progress message (e.g. 'Generating video')." },
          "renderState": { "type": "string", "description": "Current render state (e.g. 'RUNNING')." },
          "progress": { "type": "number", "description": "100 when status is completed" },
          "videoURL": { "type": "string", "format": "uri", "description": "Direct download URL for the rendered MP4 video" },
          "videoShareURL": { "type": "string", "format": "uri" },
          "videoEmbedURL": { "type": "string", "format": "uri", "description": "Embeddable URL suitable for iframes" },
          "audioURL": { "type": "string", "format": "uri", "description": "Direct download URL for the MP3 audio track" },
          "thumbnail": { "type": "string", "format": "uri" },
          "srtFile": { "type": "string", "format": "uri" },
          "txtFile": { "type": "string", "format": "uri" },
          "vttFile": { "type": "string", "format": "uri" },
          "videoDuration": { "type": "number", "description": "Total video length in seconds" },
          "encodingDuration": { "type": "number", "description": "Time taken to encode and render the video in seconds" },
          "aiCreditsUsed": { "type": "number", "description": "Total AI credits consumed for AI visual generation. Present only when the video includes scenes with `aiVisual`." },
          "error_code": { "type": "string", "description": "Present when status is failed" },
          "error_message": { "type": "string", "description": "Present when status is failed" }
        }
      },
      "StoryboardRequest": {
        "type": "object",
        "required": ["videoName", "scenes"],
        "description": "Top-level storyboard request body shared by Create Storyboard Preview and Render Storyboard Video. `scenes` is required unless `templateId` is supplied. `brandId` and `brandName` are mutually exclusive (provide at most one). The same exclusivity applies to `smartLayoutId`/`smartLayoutName` and `subtitleStyleId`/`subtitleStyleName`.",
        "properties": {
          "videoName": {
            "type": "string",
            "maxLength": 150,
            "description": "Name for the video project. Allowed characters: alphanumeric, spaces, underscores, hyphens."
          },
          "videoWidth": { "type": "integer", "description": "Video width in pixels. Must be supplied together with `videoHeight`." },
          "videoHeight": { "type": "integer", "description": "Video height in pixels. Must be supplied together with `videoWidth`." },
          "aspectRatio": {
            "type": "string",
            "enum": ["1:1", "16:9", "9:16", "4:5"],
            "description": "Video aspect ratio."
          },
          "language": {
            "type": "string",
            "enum": ["zh", "nl", "en", "fr", "de", "hi", "it", "ja", "ko", "mr", "pt", "ru", "es", "ta"],
            "description": "Language of the text content."
          },
          "saveProject": { "type": "boolean", "default": false, "description": "Whether to save the project for later editing." },
          "webhook": { "type": "string", "format": "uri", "maxLength": 500, "description": "URL to POST the job result to when finished." },
          "webhookInput": { "type": "object", "additionalProperties": true, "description": "Custom data echoed back in the webhook payload." },
          "templateId": { "type": "string", "description": "Template ID. Obtain from Get Templates API." },
          "variables": {
            "type": "object",
            "additionalProperties": { "type": "string" },
            "description": "Key/value pairs for template variables. Only valid with `templateId`."
          },
          "brandId": { "type": "string", "description": "Brand ID to apply. Mutually exclusive with `brandName`." },
          "brandName": { "type": "string", "description": "Brand name to apply. Mutually exclusive with `brandId`." },
          "smartLayoutId": { "type": "string", "description": "Smart layout ID. Mutually exclusive with `smartLayoutName`." },
          "smartLayoutName": { "type": "string", "description": "Smart layout name. Mutually exclusive with `smartLayoutId`." },
          "storyboardVersion": {
            "type": "string",
            "description": "Experimental. Set to `v3` to use the latest Pictory storyboard. Omit for classic storyboard.",
            "enum": ["v3"]
          },
          "subtitleStyleId": { "type": "string", "description": "Saved text style ID for subtitles. Mutually exclusive with `subtitleStyleName`." },
          "subtitleStyleName": { "type": "string", "description": "Saved text style name for subtitles. Mutually exclusive with `subtitleStyleId`." },
          "subtitleStyle": { "$ref": "#/components/schemas/SubtitleStyle" },
          "awsConnectionId": { "type": "string", "maxLength": 500, "description": "AWS connection ID for private S3 assets." },
          "vimeoConnectionId": { "type": "string", "maxLength": 500, "description": "Vimeo connection ID for direct Vimeo uploads." },
          "destinations": {
            "type": "array",
            "maxItems": 5,
            "items": { "$ref": "#/components/schemas/Destination" },
            "description": "Destinations to upload the generated video to."
          },
          "voiceOver": { "$ref": "#/components/schemas/VoiceOver" },
          "backgroundMusic": { "$ref": "#/components/schemas/BackgroundMusic" },
          "logo": { "$ref": "#/components/schemas/Logo" },
          "avatar": { "$ref": "#/components/schemas/Avatar" },
          "scenes": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Scene" },
            "description": "Array of scenes. Required unless `templateId` is supplied."
          },
          "payloadUrl": { "type": "string", "description": "Optional URL to fetch a payload (backend-supported field)." },
          "projectMetadata": { "type": "object", "additionalProperties": true, "description": "Arbitrary metadata stored with the saved project." }
        }
      },
      "VoiceOver": {
        "type": "object",
        "required": ["enabled"],
        "description": "Voice-over configuration. Use `aiVoices` OR `externalVoice` (mutually exclusive).",
        "properties": {
          "enabled": { "type": "boolean" },
          "aiVoices": {
            "type": "array",
            "maxItems": 10,
            "items": { "$ref": "#/components/schemas/AiVoice" }
          },
          "externalVoice": { "$ref": "#/components/schemas/ExternalVoice" }
        }
      },
      "AiVoice": {
        "type": "object",
        "required": ["speaker"],
        "properties": {
          "speaker": { "type": "string", "description": "Voice speaker name (e.g. 'Brian', 'Emma'). Obtain available voices from the Get Voiceover Tracks API." },
          "speed": { "type": "number", "minimum": 50, "maximum": 200, "default": 100 },
          "amplificationLevel": { "type": "number", "minimum": -1, "maximum": 1, "default": 0 },
          "premiumVoiceSettings": { "$ref": "#/components/schemas/PremiumVoiceSettings" }
        }
      },
      "PremiumVoiceSettings": {
        "type": "object",
        "description": "ElevenLabs premium voice settings.",
        "properties": {
          "modelId": {
            "type": "string",
            "enum": [
              "eleven_v3",
              "eleven_multilingual_v2",
              "eleven_flash_v2_5",
              "eleven_turbo_v2_5",
              "eleven_turbo_v2",
              "eleven_flash_v2",
              "eleven_multilingual_v1",
              "eleven_monolingual_v1"
            ]
          },
          "stability": { "type": "string", "description": "Voice stability as percentage e.g. '50%'." },
          "similarityBoost": { "type": "string", "description": "Similarity boost as percentage e.g. '75%'." },
          "style": { "type": "string", "description": "Voice style as percentage e.g. '50%'." },
          "useSpeakerBoost": { "type": "boolean" }
        }
      },
      "ExternalVoice": {
        "type": "object",
        "required": ["voiceUrl"],
        "properties": {
          "voiceUrl": { "type": "string", "format": "uri" },
          "syncVoice": { "type": "boolean", "description": "Auto-sync the external voice with video subtitles." },
          "amplificationLevel": { "type": "number", "minimum": -1, "maximum": 1, "default": 0 }
        }
      },
      "BackgroundMusic": {
        "type": "object",
        "required": ["enabled"],
        "description": "Background music configuration. `autoMusic` and `musicUrl` are mutually exclusive.",
        "properties": {
          "enabled": { "type": "boolean" },
          "autoMusic": { "type": "boolean" },
          "musicUrl": { "type": "string", "format": "uri" },
          "volume": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.5 },
          "clips": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "object",
              "required": ["start", "end"],
              "properties": {
                "start": { "type": "number", "description": "Start time in seconds" },
                "end": { "type": "number", "description": "End time in seconds" }
              }
            }
          }
        }
      },
      "Logo": {
        "type": "object",
        "required": ["url"],
        "properties": {
          "url": { "type": "string", "format": "uri" },
          "position": { "$ref": "#/components/schemas/Position" },
          "width": { "type": "string", "description": "Logo width as a percentage e.g. '15%'." }
        }
      },
      "Avatar": {
        "type": "object",
        "required": ["avatarId"],
        "description": "Avatar configuration for presenter-style videos. Avatars require voice-over to be enabled.",
        "properties": {
          "avatarId": { "type": "string", "description": "Account-scoped avatar identifier. MUST be discovered at runtime via `GET /v1/avatars` — do not hard-code values. Available avatars vary per account." },
          "position": { "$ref": "#/components/schemas/Position" },
          "width": { "type": "string", "default": "20%", "description": "Avatar width as integer percentage 0-100%." },
          "borderRadius": { "type": "string", "enum": ["0", "50", "100"], "description": "Corner rounding: 0 (square), 50 (rounded), 100 (circular)." },
          "borderColor": { "type": "string", "description": "Border color in RGBA format." },
          "borderThickness": { "type": "number", "minimum": 0 },
          "backgroundColor": { "type": "string", "description": "Background color in RGBA format." },
          "hide": { "type": "boolean", "default": false, "description": "Hide the avatar globally. Can be overridden per scene." }
        }
      },
      "Position": {
        "type": "string",
        "enum": [
          "top-left", "top-center", "top-right",
          "center-left", "center-center", "center-right",
          "bottom-left", "bottom-center", "bottom-right"
        ],
        "description": "Preset 3x3 grid position."
      },
      "SubtitleStyle": {
        "type": "object",
        "description": "Inline subtitle style. Can be applied at the video level or per scene.",
        "properties": {
          "fontUrl": { "type": "string", "format": "uri" },
          "fontFamily": { "type": "string", "description": "Font family name. Required when `fontUrl` is provided." },
          "fontSize": { "type": "integer", "minimum": 1 },
          "color": { "type": "string", "description": "RGBA color." },
          "backgroundColor": { "type": "string" },
          "shadowColor": { "type": "string" },
          "shadowWidth": { "type": "string" },
          "keywordColor": { "type": "string" },
          "position": { "$ref": "#/components/schemas/Position" },
          "alignment": { "type": "string", "enum": ["left", "center", "right"] },
          "decorations": {
            "type": "array",
            "items": { "type": "string", "enum": ["bold", "underline", "italics", "linethrough"] }
          },
          "case": { "type": "string", "enum": ["uppercase", "lowercase", "capitalize", "smallcapitalize"] },
          "paragraphWidth": { "type": "string" },
          "animations": {
            "type": "array",
            "maxItems": 2,
            "items": { "$ref": "#/components/schemas/TextAnimation" },
            "description": "Max 2 animations: one entry, one exit."
          }
        }
      },
      "TextAnimation": {
        "type": "object",
        "required": ["name", "type", "speed"],
        "properties": {
          "name": {
            "type": "string",
            "enum": ["none", "fade", "drift", "wipe", "text reveal", "elastic", "typewriter", "blur", "bulletin"]
          },
          "type": { "type": "string", "enum": ["entry", "exit"] },
          "speed": { "type": "string", "enum": ["slow", "medium", "fast", "custom"] },
          "customSpeedValue": { "type": "number", "minimum": 0.5, "description": "Required when speed is 'custom'." },
          "direction": { "type": "string", "enum": ["up", "down", "left", "right"] },
          "writingStyle": { "type": "string", "enum": ["character", "word", "line", "paragraph"] },
          "futureWords": {
            "type": "string",
            "enum": ["hidden", "subtle", "prominent"],
            "description": "Only available with `fade` and `blur` entry animations."
          }
        }
      },
      "Destination": {
        "type": "object",
        "description": "Destination configuration. Currently `vimeo` and `s3` types are supported.",
        "oneOf": [
          { "$ref": "#/components/schemas/VimeoDestination" },
          { "$ref": "#/components/schemas/S3Destination" }
        ]
      },
      "VimeoDestination": {
        "type": "object",
        "required": ["type"],
        "properties": {
          "type": { "type": "string", "enum": ["vimeo"] },
          "folder_uri": { "type": "string" },
          "content_rating": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": ["violence", "drugs", "language", "nudity", "advertisement", "safe", "unrated"]
            }
          },
          "privacy": { "$ref": "#/components/schemas/VimeoPrivacy" }
        }
      },
      "VimeoPrivacy": {
        "type": "object",
        "properties": {
          "view": { "type": "string", "enum": ["anybody", "contacts", "disable", "nobody", "password", "unlisted", "users"] },
          "embed": { "type": "string", "enum": ["private", "public", "whitelist"] },
          "comments": { "type": "string", "enum": ["anybody", "contacts", "nobody"] },
          "add": { "type": "boolean" },
          "download": { "type": "boolean" }
        }
      },
      "S3Destination": {
        "type": "object",
        "required": ["type", "bucket", "region", "key"],
        "properties": {
          "type": { "type": "string", "enum": ["s3"] },
          "bucket": { "type": "string" },
          "region": { "type": "string" },
          "key": { "type": "string" }
        }
      },
      "Scene": {
        "type": "object",
        "description": "Each scene must contain exactly one content source: `story`, `storyCoPilot`, `blogUrl`, `pptUrl`, `audioUrl`, or `videoUrl`.",
        "properties": {
          "story": { "type": "string", "maxLength": 15000, "description": "Plain text content for the scene." },
          "storyCoPilot": { "$ref": "#/components/schemas/StoryCoPilot" },
          "blogUrl": { "type": "string", "format": "uri", "maxLength": 3000 },
          "pptUrl": { "type": "string", "format": "uri", "maxLength": 3000 },
          "audioUrl": { "type": "string", "format": "uri", "maxLength": 3000, "description": "Requires `audioLanguage` and `transcript`." },
          "videoUrl": { "type": "string", "format": "uri", "maxLength": 3000, "description": "Requires `audioLanguage` and `transcript`." },
          "createSceneOnNewLine": { "type": "boolean", "default": false },
          "createSceneOnEndOfSentence": { "type": "boolean", "default": false },
          "maxSubtitleLines": {
            "type": "integer",
            "minimum": 1,
            "maximum": 4,
            "description": "Caption lines shown simultaneously. Cannot be used with smart layouts."
          },
          "highlightKeywords": { "type": "boolean" },
          "hideSubtitles": { "type": "boolean" },
          "minimumDuration": { "type": "number", "description": "Minimum scene duration in seconds." },
          "endPauseDuration": { "type": "number", "description": "Pause at end of scene in seconds." },
          "sceneTransition": {
            "type": "string",
            "enum": ["none", "wipeup", "wipedown", "wipeleft", "wiperight", "smoothleft", "smoothright", "radial", "circlecrop", "hblur", "fade"]
          },
          "audioLanguage": {
            "type": "string",
            "enum": [
              "en-US", "en-AU", "en-GB", "en-IN", "en-IE", "en-AB", "en-WL",
              "fr-CA", "fr-FR", "de-CH", "de-DE", "it-IT",
              "es-ES", "es-US", "nl-NL", "pt-BR", "ja-JP", "ko-KR",
              "ru-RU", "hi-IN", "ta-IN", "mr-IN"
            ],
            "description": "Required when `audioUrl` or `videoUrl` is provided."
          },
          "useSpeakerNotes": { "type": "boolean", "description": "Only valid with `pptUrl`." },
          "animatePPT": { "type": "boolean", "description": "Enable AI-generated slide animations (zoom, pan, fade) for the PowerPoint scene. Only valid with `pptUrl`." },
          "isSSMLStory": { "type": "boolean", "default": false, "description": "Story contains SSML markup. Only valid with `story`." },
          "caption": { "type": "string", "maxLength": 3000, "description": "Separate caption text. Requires `story`." },
          "captionLanguage": {
            "type": "string",
            "enum": ["zh", "nl", "en", "fr", "de", "hi", "it", "ja", "ko", "mr", "pt", "ru", "es", "ta"],
            "description": "Only valid with `caption`."
          },
          "subtitleStyleId": { "type": "string" },
          "subtitleStyleName": { "type": "string" },
          "subtitleStyle": { "$ref": "#/components/schemas/SubtitleStyle" },
          "voiceOver": { "$ref": "#/components/schemas/VoiceOver" },
          "backgroundMusic": {
            "type": "object",
            "required": ["enabled"],
            "properties": { "enabled": { "type": "boolean" } }
          },
          "avatar": { "$ref": "#/components/schemas/SceneAvatarOverride" },
          "background": { "$ref": "#/components/schemas/Background" },
          "backgroundBrolls": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/BackgroundBroll" }
          },
          "backgroundCorpus": {
            "type": "array",
            "maxItems": 100,
            "items": { "$ref": "#/components/schemas/BackgroundCorpusItem" }
          },
          "transcript": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/TranscriptSentence" },
            "description": "Transcript of input video/audio. Required for `audioUrl`/`videoUrl`."
          },
          "mediaRepurposeSettings": { "$ref": "#/components/schemas/MediaRepurposeSettings" },
          "templateOverride": { "$ref": "#/components/schemas/TemplateOverride" },
          "elements": {
            "type": "array",
            "maxItems": 20,
            "items": { "$ref": "#/components/schemas/SceneElement" },
            "description": "Overlay elements (shapes, text, videos, images) placed on top of the scene background."
          }
        }
      },
      "StoryCoPilot": {
        "type": "object",
        "required": ["prompt"],
        "properties": {
          "prompt": { "type": "string", "minLength": 1, "maxLength": 5000 },
          "videoType": {
            "type": "string",
            "enum": ["Explainer", "Marketing", "Internal Communication", "Tutorial", "Product"],
            "default": "Explainer"
          },
          "duration": { "type": "integer", "minimum": 1, "maximum": 600 },
          "platform": {
            "type": "string",
            "enum": ["YouTube", "TikTok", "Instagram", "Facebook", "LinkedIn", "Twitter"]
          },
          "tone": {
            "type": "string",
            "enum": ["professional", "casual", "friendly", "informative", "persuasive", "exciting", "educational", "humorous", "serious", "conversational"]
          }
        }
      },
      "SceneAvatarOverride": {
        "type": "object",
        "description": "Scene-level avatar override. `avatarId` cannot be changed at the scene level.",
        "properties": {
          "position": { "$ref": "#/components/schemas/Position" },
          "top": { "type": "string", "description": "Distance from top as integer percentage. Cannot be used with `position`." },
          "left": { "type": "string", "description": "Distance from left as integer percentage. Cannot be used with `position`." },
          "width": { "type": "string" },
          "borderRadius": { "type": "string", "enum": ["0", "50", "100"] },
          "borderColor": { "type": "string" },
          "borderThickness": { "type": "number", "minimum": 0 },
          "backgroundColor": { "type": "string" },
          "hide": { "type": "boolean" }
        }
      },
      "Background": {
        "type": "object",
        "description": "Scene background. Must contain exactly one of `visualUrl`, `color`, or `aiVisual`.",
        "properties": {
          "visualUrl": { "type": "string", "format": "uri" },
          "color": { "type": "string", "description": "Solid background color in RGBA, or hex format." },
          "colorOverlay": { "$ref": "#/components/schemas/ColorOverlay" },
          "aiVisual": { "$ref": "#/components/schemas/AiVisual" },
          "type": { "type": "string", "enum": ["video", "image"], "description": "Required when `aiVisual` is provided." },
          "searchFilter": { "$ref": "#/components/schemas/SearchFilter" },
          "clips": {
            "type": "array",
            "items": {
              "type": "object",
              "required": ["start", "end"],
              "properties": {
                "start": { "type": "number" },
                "end": { "type": "number" }
              }
            }
          },
          "settings": {
            "type": "object",
            "properties": {
              "mute": { "type": "boolean" },
              "loop": { "type": "boolean" },
              "zoomAndPan": { "type": "boolean", "description": "Mutually exclusive with `kenBurnsEffect`." },
              "kenBurnsEffect": { "type": "boolean", "description": "Images only. Mutually exclusive with `zoomAndPan`." }
            }
          }
        }
      },
      "AiVisual": {
        "type": "object",
        "required": ["model"],
        "properties": {
          "prompt": { "type": "string", "maxLength": 500 },
          "model": {
            "type": "string",
            "description": "AI model. Image models: flux-schnell, seedream3.0, nanobanana, nanobanana-pro. Video models: pixverse5.5, veo3.1_fast, veo3.1.",
            "enum": ["flux-schnell", "seedream3.0", "nanobanana", "nanobanana-pro", "pixverse5.5", "veo3.1_fast", "veo3.1"]
          },
          "mediaStyle": {
            "type": "string",
            "enum": ["photorealistic", "artistic", "cartoon", "minimalist", "vintage", "futuristic"],
            "description": "Image type only."
          },
          "videoDuration": { "type": "string", "description": "Video clip duration; valid values depend on model. pixverse5.5: 5s/8s/10s; veo3.1*: 4s/6s/8s." },
          "visualContinuity": { "type": "boolean", "description": "Enable visual continuity between consecutive AI-generated scenes." },
          "firstFrameImageUrl": { "type": "string", "format": "uri", "description": "Video type only. Mutually exclusive with `referenceImageUrls`." },
          "referenceImageUrl": { "type": "string", "format": "uri", "description": "Image type only." },
          "referenceImageUrls": {
            "type": "array",
            "minItems": 1,
            "maxItems": 2,
            "items": { "type": "string", "format": "uri" },
            "description": "Video type only. Mutually exclusive with `firstFrameImageUrl`."
          }
        }
      },
      "SearchFilter": {
        "type": "object",
        "properties": {
          "category": { "type": "string" },
          "query": { "type": "string", "maxLength": 3000 },
          "keywords": {
            "type": "array",
            "maxItems": 10,
            "items": { "type": "string", "minLength": 2, "maxLength": 100 }
          },
          "libraries": {
            "type": "array",
            "items": { "type": "string", "enum": ["story_blocks", "getty"] }
          }
        }
      },
      "ColorOverlay": {
        "type": "object",
        "required": ["color"],
        "description": "Semi-transparent color tint rendered on top of the background or media element. Transparency is controlled only by `opacity`; an RGBA color's alpha channel is ignored.",
        "properties": {
          "color": { "type": "string", "description": "Tint color in RGB, RGBA, or hex format." },
          "opacity": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.4, "description": "Tint strength from 0 (invisible) to 1 (solid)." }
        }
      },
      "SceneElement": {
        "type": "object",
        "required": ["type"],
        "description": "Overlay element placed on top of the scene background. `shape` and `text` are decorative overlays; `video` and `image` are media overlays sourced from exactly one of `visualUrl`, `searchFilter`, or `aiVisual`. Use `position` or `top`/`left`, not both.",
        "properties": {
          "type": { "type": "string", "enum": ["shape", "text", "video", "image"] },
          "name": { "type": "string", "description": "Shape only (required): shape name, e.g. rectangle, circle, badge-3." },
          "fill": { "type": "string", "description": "Shape only: fill color." },
          "stroke": { "type": "string", "description": "Shape only: outline color." },
          "strokeWidth": { "type": "number", "description": "Shape only: outline thickness in pixels." },
          "borderRadius": { "type": "number", "description": "Shape only: corner radius (rectangle only)." },
          "text": { "type": "string", "maxLength": 2000, "description": "Text only (required): the text to display." },
          "textVariant": { "type": "string", "enum": ["heading", "subheading", "body"], "description": "Text only: decorative preset. Defaults to body." },
          "style": { "$ref": "#/components/schemas/SubtitleStyle", "description": "Text only: style overrides." },
          "visualUrl": { "type": "string", "format": "uri", "description": "Media only: URL of the video or image file to overlay." },
          "searchFilter": {
            "type": "object",
            "required": ["query"],
            "description": "Media only: stock library search. If no match is found, the element is skipped.",
            "properties": {
              "query": { "type": "string", "minLength": 2, "maxLength": 3000, "description": "What to search for, in plain language." }
            }
          },
          "aiVisual": { "$ref": "#/components/schemas/AiVisual", "description": "Media only: generate the element with AI. Consumes AI Credits according to the selected model's rate." },
          "settings": {
            "type": "object",
            "description": "Video elements only: playback settings.",
            "properties": {
              "loop": { "type": "boolean", "default": true },
              "mute": { "type": "boolean", "default": true }
            }
          },
          "colorOverlay": { "$ref": "#/components/schemas/ColorOverlay", "description": "Media only: color tint applied to this element." },
          "position": {
            "type": "string",
            "enum": ["top-left", "top-center", "top-right", "center-left", "center", "center-center", "center-right", "bottom-left", "bottom-center", "bottom-right"],
            "description": "Anchor preset. Mutually exclusive with `top`/`left`."
          },
          "top": { "type": "string", "description": "Vertical offset percentage, e.g. \"10%\". Mutually exclusive with `position`." },
          "left": { "type": "string", "description": "Horizontal offset percentage, e.g. \"10%\". Mutually exclusive with `position`." },
          "width": { "type": "string", "description": "Element width as a percentage of the canvas, e.g. \"30%\". Media elements without a width are sized automatically to stay fully on screen." }
        }
      },
      "BackgroundBroll": {
        "allOf": [
          { "$ref": "#/components/schemas/Background" },
          {
            "type": "object",
            "properties": {
              "brollClip": {
                "type": "object",
                "required": ["start", "end"],
                "properties": {
                  "start": { "type": "number" },
                  "end": { "type": "number" }
                }
              }
            }
          }
        ]
      },
      "BackgroundCorpusItem": {
        "type": "object",
        "properties": {
          "visualUrl": { "type": "string", "format": "uri" },
          "type": { "type": "string", "enum": ["image"] },
          "prefer": { "type": "boolean" }
        }
      },
      "TranscriptSentence": {
        "type": "object",
        "required": ["words"],
        "properties": {
          "speakerId": { "type": "integer" },
          "words": {
            "type": "array",
            "minItems": 1,
            "items": { "$ref": "#/components/schemas/TranscriptWord" }
          }
        }
      },
      "TranscriptWord": {
        "type": "object",
        "required": ["word", "start_time", "end_time"],
        "properties": {
          "word": { "type": "string" },
          "start_time": { "type": "number", "minimum": 0 },
          "end_time": { "type": "number", "description": "Must be >= start_time." },
          "is_pause": { "type": "boolean" },
          "is_filler": { "type": "boolean" },
          "speakerId": { "type": "integer" }
        }
      },
      "MediaRepurposeSettings": {
        "type": "object",
        "description": "Only valid with `audioUrl` or `videoUrl`.",
        "properties": {
          "highlightLength": { "type": "number", "minimum": 5, "maximum": 180 },
          "removeFillerWords": { "type": "boolean" },
          "removeSilences": { "type": "boolean" },
          "silenceThresholdSeconds": { "type": "number", "minimum": 0, "maximum": 10 }
        }
      },
      "TemplateOverride": {
        "type": "object",
        "description": "Configuration for manipulating template scenes. Only available when `templateId` is provided.",
        "properties": {
          "sceneId": { "type": "string" },
          "scenePosition": { "type": "integer", "minimum": 1 },
          "newScenePosition": { "type": "integer", "minimum": 1 },
          "insertAfterSceneId": { "type": "string" },
          "insertBeforeSceneId": { "type": "string" },
          "replaceSceneId": { "type": "string" },
          "replaceScenePosition": { "type": "integer", "minimum": 1 },
          "baseSceneId": { "type": "string" },
          "baseScenePosition": { "type": "integer", "minimum": 1 },
          "deleteScene": { "type": "boolean" },
          "copyScene": { "type": "boolean" },
          "subtitles": {
            "type": "array",
            "maxItems": 500,
            "items": {
              "type": "object",
              "required": ["text"],
              "properties": {
                "text": { "type": "string", "minLength": 1, "maxLength": 40000 },
                "minimumDuration": { "type": "number" },
                "styleId": { "type": "string" },
                "styleName": { "type": "string" },
                "style": { "$ref": "#/components/schemas/SubtitleStyle" }
              }
            }
          },
          "layers": {
            "type": "array",
            "maxItems": 500,
            "items": {
              "type": "array",
              "minItems": 1,
              "maxItems": 500,
              "items": {
                "type": "object",
                "properties": {
                  "layerId": { "type": "string", "maxLength": 100 },
                  "type": { "type": "string", "enum": ["text", "image", "video"] },
                  "text": { "type": "string", "maxLength": 40000 },
                  "url": { "type": "string", "maxLength": 3000 },
                  "styleId": { "type": "string" },
                  "styleName": { "type": "string" },
                  "style": { "$ref": "#/components/schemas/SubtitleStyle" },
                  "visualLibraryId": { "type": "string", "minLength": 1 },
                  "deleteLayer": { "type": "boolean" }
                }
              }
            }
          }
        }
      }
    }
  }
}
