{
  "openapi": "3.1.0",
  "info": {
    "title": "Puppetry Developer API Beta",
    "description": "Live Puppetry Developer API beta for Puppetry Voice API access, hosted audio uploads, usage, and credit-backed text/audio-to-video jobs.",
    "version": "2026-05-11",
    "contact": {
      "name": "Puppetry Support",
      "url": "https://www.puppetry.com",
      "email": "support@puppetry.com"
    },
    "termsOfService": "https://www.puppetry.com/tos",
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://www.puppetry.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "developerApiKey": []
    }
  ],
  "paths": {
    "/api/v1/voices/puppetry": {
      "get": {
        "operationId": "listPuppetryVoices",
        "x-openai-isConsequential": false,
        "summary": "List Puppetry voices",
        "description": "Returns the public Puppetry voice catalog available to Developer API keys with the voices:read scope. Each voice includes preview_url for fixed sample audio.",
        "tags": ["Voices"],
        "security": [
          {
            "developerApiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Public Puppetry voices",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VoiceListResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/tts/puppetry": {
      "post": {
        "operationId": "createPuppetrySpeech",
        "x-openai-isConsequential": true,
        "summary": "Generate speech with a Puppetry voice",
        "description": "Creates a hosted WAV audio file from text using a public Puppetry voice. Requires the tts:create scope.",
        "tags": ["Text to Speech"],
        "security": [
          {
            "developerApiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TtsRequest"
              },
              "examples": {
                "speech": {
                  "summary": "Generate speech",
                  "value": {
                    "voice_id": "puppetry-af_bella",
                    "text": "Hello from Puppetry.",
                    "speed": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Generated audio",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TtsResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Unknown Puppetry voice",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "voiceNotFound": {
                    "value": {
                      "error": "voice_not_found",
                      "message": "Unknown Puppetry voice"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "503": {
            "$ref": "#/components/responses/TemporarilyUnavailable"
          }
        }
      }
    },
    "/api/v1/uploads/audio-url": {
      "post": {
        "operationId": "createHostedAudioUploadUrl",
        "x-openai-isConsequential": false,
        "summary": "Create a signed hosted audio upload URL",
        "description": "Creates a short-lived signed PUT URL for existing audio and a short-lived read URL for the uploaded asset. Send an Idempotency-Key header, or idempotencyKey/idempotency_key in the JSON body when custom headers are unavailable, so a lost response does not reserve duplicate hosted-upload quota. For audio-to-video workflows, send require_video_readiness=true to fail before reserving upload quota when video credits or active slots are blocked; active-slot blockers and retryable transient failures include Retry-After and X-Puppetry-Next-Poll-At so agents can pace the next reservation attempt. Idempotent replays of an existing pending upload still return a fresh signed URL. Requires the uploads:create scope.",
        "tags": ["Audio Uploads"],
        "security": [
          {
            "developerApiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AudioUploadRequest"
              },
              "examples": {
                "audioUpload": {
                  "summary": "Create upload URL",
                  "value": {
                    "contentType": "audio/mpeg",
                    "contentLength": 1048576,
                    "idempotencyKey": "upload-demo-1",
                    "requireVideoReadiness": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Signed upload and read URLs",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudioUploadUrlResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "Idempotency-Key replay is in progress or conflicts with existing upload metadata.",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-Puppetry-Next-Poll-At": {
                "$ref": "#/components/headers/NextPollAt"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "idempotencyInProgress": {
                    "value": {
                      "error": "idempotency_key_in_progress",
                      "message": "An upload URL with this Idempotency-Key is already being created. Retry this request shortly.",
                      "retryable": true,
                      "retry_after_seconds": 5,
                      "retryAfter": 5,
                      "retryAfterSeconds": 5,
                      "next_poll_at": "2026-06-22T08:50:05.000Z",
                      "nextPollAt": "2026-06-22T08:50:05.000Z",
                      "details": {
                        "retryable": true,
                        "retry_after_seconds": 5,
                        "retryAfter": 5,
                        "retryAfterSeconds": 5,
                        "next_poll_at": "2026-06-22T08:50:05.000Z",
                        "nextPollAt": "2026-06-22T08:50:05.000Z"
                      }
                    }
                  },
                  "idempotencyKeyConflict": {
                    "value": {
                      "error": "idempotency_key_conflict",
                      "message": "Idempotency-Key was already used with different upload metadata",
                      "details": {
                        "retryable": false,
                        "retry_blocked_reason": "Idempotency-Key was already used with different upload metadata",
                        "retryBlockedReason": "Idempotency-Key was already used with different upload metadata",
                        "retry_block_code": "AUDIO_UPLOAD_IDEMPOTENCY_KEY_CONFLICT",
                        "retryBlockCode": "AUDIO_UPLOAD_IDEMPOTENCY_KEY_CONFLICT"
                      }
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "Request body or audio file exceeds the upload limits",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "requestBodyTooLarge": {
                    "value": {
                      "error": "payload_too_large",
                      "message": "Request body must be 8192 bytes or fewer",
                      "details": {
                        "max_body_bytes": 8192,
                        "retryable": false,
                        "retry_blocked_reason": "Request body must be 8192 bytes or fewer",
                        "retryBlockedReason": "Request body must be 8192 bytes or fewer",
                        "retry_block_code": "AUDIO_UPLOAD_PAYLOAD_TOO_LARGE",
                        "retryBlockCode": "AUDIO_UPLOAD_PAYLOAD_TOO_LARGE"
                      }
                    }
                  },
                  "fileTooLarge": {
                    "value": {
                      "error": "file_too_large",
                      "message": "Audio uploads are limited to 50MB",
                      "details": {
                        "max_file_bytes": 52428800,
                        "retryable": false,
                        "retry_blocked_reason": "Audio uploads are limited to 50MB",
                        "retryBlockedReason": "Audio uploads are limited to 50MB",
                        "retry_block_code": "AUDIO_UPLOAD_FILE_TOO_LARGE",
                        "retryBlockCode": "AUDIO_UPLOAD_FILE_TOO_LARGE"
                      }
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "Request Content-Type must be application/json",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "unsupportedMediaType": {
                    "value": {
                      "error": "unsupported_media_type",
                      "message": "Content-Type must be application/json",
                      "details": {
                        "retryable": false,
                        "retry_blocked_reason": "Content-Type must be application/json",
                        "retryBlockedReason": "Content-Type must be application/json",
                        "retry_block_code": "UNSUPPORTED_AUDIO_UPLOAD_MEDIA_TYPE",
                        "retryBlockCode": "UNSUPPORTED_AUDIO_UPLOAD_MEDIA_TYPE"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Video generation active slots are full, or the Developer API rate limit was exceeded.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-Puppetry-Next-Poll-At": {
                "$ref": "#/components/headers/NextPollAt"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "videoSlotsFull": {
                    "value": {
                      "error": "concurrent_job_limit_reached",
                      "message": "Developer API video generation is at its active job limit. Retry after the active job slot expires.",
                      "retryable": true,
                      "retry_after_seconds": 512,
                      "retryAfter": 512,
                      "retryAfterSeconds": 512,
                      "next_poll_at": "2026-06-22T08:50:30.000Z",
                      "nextPollAt": "2026-06-22T08:50:30.000Z",
                      "details": {
                        "retryable": true,
                        "code": "concurrent_job_limit_reached",
                        "reason": "concurrent_job_limit_reached",
                        "video_generation": {
                          "can_create": false,
                          "canCreate": false,
                          "reason": "concurrent_job_limit_reached",
                          "active_jobs": 2,
                          "activeJobs": 2,
                          "available_slots": 0,
                          "availableSlots": 0,
                          "retry_after_seconds": 512,
                          "retryAfter": 512,
                          "retryAfterSeconds": 512,
                          "next_poll_at": "2026-06-22T08:50:30.000Z",
                          "nextPollAt": "2026-06-22T08:50:30.000Z"
                        },
                        "videoGeneration": {
                          "can_create": false,
                          "canCreate": false,
                          "reason": "concurrent_job_limit_reached",
                          "active_jobs": 2,
                          "activeJobs": 2,
                          "available_slots": 0,
                          "availableSlots": 0,
                          "retry_after_seconds": 512,
                          "retryAfter": 512,
                          "retryAfterSeconds": 512,
                          "next_poll_at": "2026-06-22T08:50:30.000Z",
                          "nextPollAt": "2026-06-22T08:50:30.000Z"
                        }
                      }
                    }
                  },
                  "rateLimited": {
                    "value": {
                      "error": "rate_limited",
                      "message": "Developer API beta limit exceeded: 10 requests per minute.",
                      "retryable": true,
                      "retry_after_seconds": 30,
                      "retryAfter": 30,
                      "retryAfterSeconds": 30,
                      "next_poll_at": "2026-06-22T08:50:30.000Z",
                      "nextPollAt": "2026-06-22T08:50:30.000Z",
                      "details": {
                        "retryable": true,
                        "retry_after_seconds": 30,
                        "retryAfter": 30,
                        "retryAfterSeconds": 30,
                        "next_poll_at": "2026-06-22T08:50:30.000Z",
                        "nextPollAt": "2026-06-22T08:50:30.000Z"
                      }
                    }
                  }
                }
              }
            }
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "503": {
            "$ref": "#/components/responses/TemporarilyUnavailable"
          }
        }
      }
    },
    "/api/v1/usage": {
      "get": {
        "operationId": "getDeveloperApiUsage",
        "x-openai-isConsequential": false,
        "summary": "Get Developer API usage",
        "description": "Returns the authenticated workspace's current video credit balance plus monthly credit usage for credit-backed Developer API video jobs. Requires the videos:read scope. When video generation is blocked by active job slots or usage data is temporarily unavailable, the response includes Retry-After and X-Puppetry-Next-Poll-At so agents can pace the next create or usage attempt.",
        "tags": ["Usage"],
        "security": [
          {
            "developerApiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "Current Developer API usage",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-Puppetry-Next-Poll-At": {
                "$ref": "#/components/headers/NextPollAt"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/TemporarilyUnavailable"
          }
        }
      }
    },
    "/api/v1/videos/text": {
      "post": {
        "operationId": "createVideoFromText",
        "x-openai-isConsequential": true,
        "summary": "Create a video from text",
        "description": "Queues a credit-backed text-to-video job using a hosted image URL and a Puppetry voice. Requires the videos:create scope. Accepted queued responses and non-terminal Idempotency-Key replays include Retry-After, X-Puppetry-Next-Poll-At, plus absolute Location/Content-Location, Puppetry-Operation-Id, Puppetry-Request-Id, and X-Puppetry-Credits-* headers when debit metadata is available so clients can pace, locate, trace, and reconcile the first status poll.",
        "tags": ["Videos"],
        "security": [
          {
            "developerApiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TextVideoRequest"
              },
              "examples": {
                "textVideo": {
                  "summary": "Create text video",
                  "value": {
                    "image_url": "https://example.com/portrait.png",
                    "text": "Hello from Puppetry.",
                    "voice_id": "puppetry-af_heart",
                    "language": "en",
                    "expressiveness": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Terminal video job returned from an Idempotency-Key replay without charging another credit. Includes absolute Location/Content-Location, Puppetry-Operation-Id, Puppetry-Request-Id, and X-Puppetry-Credits-* headers when debit metadata is available for the replayed job; retryable failed replays also include Retry-After and X-Puppetry-Next-Poll-At.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-Puppetry-Next-Poll-At": {
                "$ref": "#/components/headers/NextPollAt"
              },
              "Location": {
                "$ref": "#/components/headers/VideoJobStatusLocation"
              },
              "Content-Location": {
                "$ref": "#/components/headers/VideoJobStatusLocation"
              },
              "Puppetry-Operation-Id": {
                "$ref": "#/components/headers/VideoJobOperationId"
              },
              "X-Puppetry-Operation-Id": {
                "$ref": "#/components/headers/VideoJobOperationId"
              },
              "Puppetry-Request-Id": {
                "$ref": "#/components/headers/VideoJobRequestId"
              },
              "X-Puppetry-Request-Id": {
                "$ref": "#/components/headers/VideoJobRequestId"
              },
              "X-Puppetry-Credits-Required": {
                "$ref": "#/components/headers/VideoJobCreditsRequired"
              },
              "X-Puppetry-Credits-Remaining": {
                "$ref": "#/components/headers/VideoJobCreditsRemaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoJobReplayResponse"
                }
              }
            }
          },
          "202": {
            "description": "Video job queued, or a non-terminal Idempotency-Key replay returned without charging another credit. Includes Retry-After, X-Puppetry-Next-Poll-At, plus absolute Location/Content-Location, Puppetry-Operation-Id, Puppetry-Request-Id, and X-Puppetry-Credits-* headers when debit metadata is available so clients can pace, locate, trace, and reconcile the first status poll.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-Puppetry-Next-Poll-At": {
                "$ref": "#/components/headers/NextPollAt"
              },
              "Location": {
                "$ref": "#/components/headers/VideoJobStatusLocation"
              },
              "Content-Location": {
                "$ref": "#/components/headers/VideoJobStatusLocation"
              },
              "Puppetry-Operation-Id": {
                "$ref": "#/components/headers/VideoJobOperationId"
              },
              "X-Puppetry-Operation-Id": {
                "$ref": "#/components/headers/VideoJobOperationId"
              },
              "Puppetry-Request-Id": {
                "$ref": "#/components/headers/VideoJobRequestId"
              },
              "X-Puppetry-Request-Id": {
                "$ref": "#/components/headers/VideoJobRequestId"
              },
              "X-Puppetry-Credits-Required": {
                "$ref": "#/components/headers/VideoJobCreditsRequired"
              },
              "X-Puppetry-Credits-Remaining": {
                "$ref": "#/components/headers/VideoJobCreditsRemaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoJobQueuedResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Unknown Puppetry voice for text-to-video",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "voiceNotFound": {
                    "value": {
                      "error": "voice_not_found",
                      "message": "Unknown Puppetry voice. Use a voice_id from GET /api/v1/voices/puppetry.",
                      "details": {
                        "retryable": false,
                        "retry_blocked_reason": "Unknown Puppetry voice. Use a voice_id from GET /api/v1/voices/puppetry.",
                        "retryBlockedReason": "Unknown Puppetry voice. Use a voice_id from GET /api/v1/voices/puppetry.",
                        "retry_block_code": "PUPPETRY_VOICE_NOT_FOUND",
                        "retryBlockCode": "PUPPETRY_VOICE_NOT_FOUND"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "413": {
            "description": "Request body exceeds the video create limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "payloadTooLarge": {
                    "value": {
                      "error": "payload_too_large",
                      "message": "Request body must be 65536 bytes or fewer",
                      "details": {
                        "max_body_bytes": 65536,
                        "retryable": false,
                        "retry_blocked_reason": "Request body must be 65536 bytes or fewer",
                        "retryBlockedReason": "Request body must be 65536 bytes or fewer",
                        "retry_block_code": "VIDEO_CREATE_PAYLOAD_TOO_LARGE",
                        "retryBlockCode": "VIDEO_CREATE_PAYLOAD_TOO_LARGE"
                      }
                    }
                  }
                }
              }
            }
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "503": {
            "$ref": "#/components/responses/TemporarilyUnavailable"
          }
        }
      }
    },
    "/api/v1/videos/audio": {
      "post": {
        "operationId": "createVideoFromAudio",
        "x-openai-isConsequential": true,
        "summary": "Create a video from audio",
        "description": "Queues a credit-backed audio-to-video job using hosted image and audio URLs. Requires the videos:create scope. Accepted queued responses and non-terminal Idempotency-Key replays include Retry-After, X-Puppetry-Next-Poll-At, plus absolute Location/Content-Location, Puppetry-Operation-Id, Puppetry-Request-Id, and X-Puppetry-Credits-* headers when debit metadata is available so clients can pace, locate, trace, and reconcile the first status poll.",
        "tags": ["Videos"],
        "security": [
          {
            "developerApiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AudioVideoRequest"
              },
              "examples": {
                "audioVideo": {
                  "summary": "Create audio video",
                  "value": {
                    "image_url": "https://example.com/portrait.png",
                    "audio_url": "https://assets.puppetry.com/api-uploads/audio.mp3",
                    "expressiveness": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Terminal video job returned from an Idempotency-Key replay without charging another credit. Includes absolute Location/Content-Location, Puppetry-Operation-Id, Puppetry-Request-Id, and X-Puppetry-Credits-* headers when debit metadata is available for the replayed job; retryable failed replays also include Retry-After and X-Puppetry-Next-Poll-At.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-Puppetry-Next-Poll-At": {
                "$ref": "#/components/headers/NextPollAt"
              },
              "Location": {
                "$ref": "#/components/headers/VideoJobStatusLocation"
              },
              "Content-Location": {
                "$ref": "#/components/headers/VideoJobStatusLocation"
              },
              "Puppetry-Operation-Id": {
                "$ref": "#/components/headers/VideoJobOperationId"
              },
              "X-Puppetry-Operation-Id": {
                "$ref": "#/components/headers/VideoJobOperationId"
              },
              "Puppetry-Request-Id": {
                "$ref": "#/components/headers/VideoJobRequestId"
              },
              "X-Puppetry-Request-Id": {
                "$ref": "#/components/headers/VideoJobRequestId"
              },
              "X-Puppetry-Credits-Required": {
                "$ref": "#/components/headers/VideoJobCreditsRequired"
              },
              "X-Puppetry-Credits-Remaining": {
                "$ref": "#/components/headers/VideoJobCreditsRemaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoJobReplayResponse"
                }
              }
            }
          },
          "202": {
            "description": "Video job queued, or a non-terminal Idempotency-Key replay returned without charging another credit. Includes Retry-After, X-Puppetry-Next-Poll-At, plus absolute Location/Content-Location, Puppetry-Operation-Id, Puppetry-Request-Id, and X-Puppetry-Credits-* headers when debit metadata is available so clients can pace, locate, trace, and reconcile the first status poll.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-Puppetry-Next-Poll-At": {
                "$ref": "#/components/headers/NextPollAt"
              },
              "Location": {
                "$ref": "#/components/headers/VideoJobStatusLocation"
              },
              "Content-Location": {
                "$ref": "#/components/headers/VideoJobStatusLocation"
              },
              "Puppetry-Operation-Id": {
                "$ref": "#/components/headers/VideoJobOperationId"
              },
              "X-Puppetry-Operation-Id": {
                "$ref": "#/components/headers/VideoJobOperationId"
              },
              "Puppetry-Request-Id": {
                "$ref": "#/components/headers/VideoJobRequestId"
              },
              "X-Puppetry-Request-Id": {
                "$ref": "#/components/headers/VideoJobRequestId"
              },
              "X-Puppetry-Credits-Required": {
                "$ref": "#/components/headers/VideoJobCreditsRequired"
              },
              "X-Puppetry-Credits-Remaining": {
                "$ref": "#/components/headers/VideoJobCreditsRemaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoJobQueuedResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/InsufficientCredits"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "413": {
            "description": "Request body exceeds the video create limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "payloadTooLarge": {
                    "value": {
                      "error": "payload_too_large",
                      "message": "Request body must be 65536 bytes or fewer",
                      "details": {
                        "max_body_bytes": 65536,
                        "retryable": false,
                        "retry_blocked_reason": "Request body must be 65536 bytes or fewer",
                        "retryBlockedReason": "Request body must be 65536 bytes or fewer",
                        "retry_block_code": "VIDEO_CREATE_PAYLOAD_TOO_LARGE",
                        "retryBlockCode": "VIDEO_CREATE_PAYLOAD_TOO_LARGE"
                      }
                    }
                  }
                }
              }
            }
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "503": {
            "$ref": "#/components/responses/TemporarilyUnavailable"
          }
        }
      }
    },
    "/api/v1/videos/{jobId}": {
      "get": {
        "operationId": "getVideoJob",
        "x-openai-isConsequential": false,
        "summary": "Get video job status",
        "description": "Polls a Developer API video job created by the same API key. Requires the videos:read scope. Jobs created with another key, user, or org return 404. Status responses include absolute Location, Content-Location, Puppetry-Operation-Id, and Puppetry-Request-Id headers with the same poll target; queued or processing 200 responses also include Retry-After and X-Puppetry-Next-Poll-At so clients can pace polling.",
        "tags": ["Videos"],
        "security": [
          {
            "developerApiKey": []
          }
        ],
        "parameters": [
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^api_video_[A-Za-z0-9_-]+$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Video job status",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              },
              "X-Puppetry-Next-Poll-At": {
                "$ref": "#/components/headers/NextPollAt"
              },
              "Location": {
                "$ref": "#/components/headers/VideoJobStatusLocation"
              },
              "Content-Location": {
                "$ref": "#/components/headers/VideoJobStatusLocation"
              },
              "Puppetry-Operation-Id": {
                "$ref": "#/components/headers/VideoJobOperationId"
              },
              "X-Puppetry-Operation-Id": {
                "$ref": "#/components/headers/VideoJobOperationId"
              },
              "Puppetry-Request-Id": {
                "$ref": "#/components/headers/VideoJobRequestId"
              },
              "X-Puppetry-Request-Id": {
                "$ref": "#/components/headers/VideoJobRequestId"
              },
              "X-Puppetry-Credits-Required": {
                "$ref": "#/components/headers/VideoJobCreditsRequired"
              },
              "X-Puppetry-Credits-Remaining": {
                "$ref": "#/components/headers/VideoJobCreditsRemaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoJobStatusResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Video job not found, or not visible to the authenticated API key",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimitReset"
              },
              "Location": {
                "$ref": "#/components/headers/VideoJobStatusLocation"
              },
              "Content-Location": {
                "$ref": "#/components/headers/VideoJobStatusLocation"
              },
              "Puppetry-Operation-Id": {
                "$ref": "#/components/headers/VideoJobOperationId"
              },
              "X-Puppetry-Operation-Id": {
                "$ref": "#/components/headers/VideoJobOperationId"
              },
              "Puppetry-Request-Id": {
                "$ref": "#/components/headers/VideoJobRequestId"
              },
              "X-Puppetry-Request-Id": {
                "$ref": "#/components/headers/VideoJobRequestId"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "notFound": {
                    "value": {
                      "error": "job_not_found",
                      "message": "Video job not found",
                      "retryable": false,
                      "id": "api_video_missing_20260702",
                      "job_id": "api_video_missing_20260702",
                      "jobId": "api_video_missing_20260702",
                      "task_id": "api_video_missing_20260702",
                      "taskId": "api_video_missing_20260702",
                      "operation_id": "api_video_missing_20260702",
                      "operationId": "api_video_missing_20260702",
                      "request_id": "api_video_missing_20260702",
                      "requestId": "api_video_missing_20260702",
                      "status_url": "/api/v1/videos/api_video_missing_20260702",
                      "statusUrl": "/api/v1/videos/api_video_missing_20260702",
                      "retry_blocked_reason": "Video job not found or not visible to this API key",
                      "retryBlockedReason": "Video job not found or not visible to this API key",
                      "retry_block_code": "VIDEO_JOB_NOT_FOUND",
                      "retryBlockCode": "VIDEO_JOB_NOT_FOUND",
                      "details": {
                        "retryable": false,
                        "id": "api_video_missing_20260702",
                        "job_id": "api_video_missing_20260702",
                        "jobId": "api_video_missing_20260702",
                        "task_id": "api_video_missing_20260702",
                        "taskId": "api_video_missing_20260702",
                        "operation_id": "api_video_missing_20260702",
                        "operationId": "api_video_missing_20260702",
                        "request_id": "api_video_missing_20260702",
                        "requestId": "api_video_missing_20260702",
                        "status_url": "/api/v1/videos/api_video_missing_20260702",
                        "statusUrl": "/api/v1/videos/api_video_missing_20260702",
                        "retry_blocked_reason": "Video job not found or not visible to this API key",
                        "retryBlockedReason": "Video job not found or not visible to this API key",
                        "retry_block_code": "VIDEO_JOB_NOT_FOUND",
                        "retryBlockCode": "VIDEO_JOB_NOT_FOUND",
                        "content_location": "/api/v1/videos/api_video_missing_20260702",
                        "contentLocation": "/api/v1/videos/api_video_missing_20260702"
                      },
                      "content_location": "/api/v1/videos/api_video_missing_20260702",
                      "contentLocation": "/api/v1/videos/api_video_missing_20260702"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/VideoJobStatusUnavailable"
          },
          "504": {
            "$ref": "#/components/responses/VideoJobStatusTimeout"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "developerApiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Puppetry API key",
        "description": "Use the Authorization header with a live Puppetry API key: Bearer pk_live_..."
      }
    },
    "headers": {
      "RateLimitLimit": {
        "description": "Requests allowed in the current one-minute window.",
        "schema": {
          "type": "integer",
          "example": 10
        }
      },
      "RateLimitRemaining": {
        "description": "Requests remaining in the current one-minute window.",
        "schema": {
          "type": "integer",
          "example": 9
        }
      },
      "RateLimitReset": {
        "description": "Unix timestamp, in seconds, when the current rate-limit window resets.",
        "schema": {
          "type": "integer",
          "example": 1778520000
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying a limited/transient request or polling a non-terminal video job again.",
        "schema": {
          "type": "integer",
          "example": 30
        }
      },
      "NextPollAt": {
        "description": "Absolute UTC retry or poll time. Mirrors next_poll_at/nextPollAt in the JSON body.",
        "schema": {
          "type": "string",
          "format": "date-time",
          "example": "2026-06-22T08:50:30.000Z"
        }
      },
      "VideoJobStatusLocation": {
        "description": "Absolute status URL for the API video job in response headers. Mirrors the status_url/content_location JSON body fields as a fully qualified poll URL.",
        "schema": {
          "type": "string",
          "example": "https://www.puppetry.com/api/v1/videos/api_video_8b1d2f4c6a7e9f0123456789"
        }
      },
      "VideoJobOperationId": {
        "description": "Stable public operation id for the Developer API video job. Mirrors the JSON id/operation_id field and never exposes the internal animation service job id.",
        "schema": {
          "type": "string",
          "pattern": "^api_video_",
          "example": "api_video_8b1d2f4c6a7e9f0123456789"
        }
      },
      "VideoJobRequestId": {
        "description": "Stable public request id for correlating a Developer API video job with backend logs. Mirrors the JSON request_id field and never exposes the internal animation service job id.",
        "schema": {
          "type": "string",
          "pattern": "^api_video_",
          "example": "api_video_8b1d2f4c6a7e9f0123456789"
        }
      },
      "VideoJobCreditsRequired": {
        "description": "Video credits charged or required for the Developer API video create operation. Fresh create responses and status responses include this when create-time debit evidence is available.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "example": 1
        }
      },
      "VideoJobCreditsRemaining": {
        "description": "Video credit balance after the Developer API video create debit. Fresh create responses and status responses include this when create-time debit evidence is available.",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "example": 299
        }
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Optional non-blank retry key for video create and hosted audio upload URL requests. Keys are scoped to the authenticated user, org, API key, endpoint, trimmed Idempotency-Key value, and normalized request payload. Replaying the same accepted video create request returns without charging another credit; replaying the same pending hosted audio upload returns a fresh signed PUT URL without reserving duplicate quota. Reusing the key for different input returns 409.",
        "schema": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200,
          "example": "text-video-demo-1"
        }
      }
    },
    "responses": {
      "InvalidRequest": {
        "description": "Invalid request body or unsupported input",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "hostedAudioUploadUnavailable": {
                "value": {
                  "error": "hosted_audio_upload_unavailable",
                  "message": "Hosted audio upload ownership is temporarily unavailable",
                  "details": {
                    "retry_after_seconds": 217,
                    "retryAfter": 217,
                    "retryAfterSeconds": 217
                  }
                }
              },
              "hostedAudioUploadNotFound": {
                "value": {
                  "error": "invalid_request",
                  "message": "audio_url must reference a hosted API audio upload owned by this API key",
                  "details": {
                    "retryable": false,
                    "retry_blocked_reason": "audio_url must reference a hosted API audio upload owned by this API key",
                    "retryBlockedReason": "audio_url must reference a hosted API audio upload owned by this API key",
                    "retry_block_code": "HOSTED_AUDIO_UPLOAD_NOT_FOUND",
                    "retryBlockCode": "HOSTED_AUDIO_UPLOAD_NOT_FOUND"
                  }
                }
              },
              "hostedAudioUploadPending": {
                "value": {
                  "error": "invalid_request",
                  "message": "audio_url hosted upload has not finished uploading; upload the file before creating a video",
                  "details": {
                    "retryable": false,
                    "retry_blocked_reason": "audio_url hosted upload has not finished uploading; upload the file before creating a video",
                    "retryBlockedReason": "audio_url hosted upload has not finished uploading; upload the file before creating a video",
                    "retry_block_code": "HOSTED_AUDIO_UPLOAD_PENDING",
                    "retryBlockCode": "HOSTED_AUDIO_UPLOAD_PENDING"
                  }
                }
              },
              "invalidImage": {
                "value": {
                  "error": "invalid_image",
                  "message": "image_url must be a supported image",
                  "details": {
                    "retryable": false,
                    "retry_blocked_reason": "image_url must be a supported image",
                    "retryBlockedReason": "image_url must be a supported image",
                    "retry_block_code": "INVALID_VIDEO_IMAGE",
                    "retryBlockCode": "INVALID_VIDEO_IMAGE"
                  }
                }
              },
              "audioTooLong": {
                "value": {
                  "error": "audio_too_long",
                  "message": "Audio is too long (529s). Maximum supported duration is 300 seconds (5 minutes). Please use a shorter script or split into multiple videos.",
                  "details": {
                    "duration_seconds": 529.42,
                    "max_duration_seconds": 300,
                    "retryable": false,
                    "retry_blocked_reason": "Audio is too long (529s). Maximum supported duration is 300 seconds (5 minutes). Please use a shorter script or split into multiple videos.",
                    "retryBlockedReason": "Audio is too long (529s). Maximum supported duration is 300 seconds (5 minutes). Please use a shorter script or split into multiple videos.",
                    "retry_block_code": "VIDEO_CREATE_AUDIO_TOO_LONG",
                    "retryBlockCode": "VIDEO_CREATE_AUDIO_TOO_LONG"
                  }
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, malformed, invalid, or revoked API key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "unauthorized": {
                "value": {
                  "error": "unauthorized",
                  "message": "Missing or invalid Authorization header. Use: Bearer pk_live_..."
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "The API key does not include the required endpoint scope.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "forbidden": {
                "value": {
                  "error": "forbidden",
                  "message": "This API key does not have the required scope"
                }
              }
            }
          }
        }
      },
      "UnsupportedMediaType": {
        "description": "The request body media type is not supported.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "unsupportedMediaType": {
                "value": {
                  "error": "unsupported_media_type",
                  "message": "Content-Type must be application/json"
                }
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit or monthly beta quota exceeded",
        "headers": {
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimitReset"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "rateLimited": {
                "value": {
                  "error": "rate_limited",
                  "message": "Developer API beta limit exceeded: 10 requests per minute.",
                  "retryable": true,
                  "retry_after_seconds": 30,
                  "retryAfter": 30,
                  "retryAfterSeconds": 30,
                  "next_poll_at": "2026-06-22T08:50:30.000Z",
                  "nextPollAt": "2026-06-22T08:50:30.000Z",
                  "details": {
                    "retryable": true,
                    "retry_after_seconds": 30,
                    "retryAfter": 30,
                    "retryAfterSeconds": 30,
                    "next_poll_at": "2026-06-22T08:50:30.000Z",
                    "nextPollAt": "2026-06-22T08:50:30.000Z"
                  }
                }
              },
              "voiceCharacterLimit": {
                "value": {
                  "error": "voice_character_limit_exceeded",
                  "message": "Monthly Puppetry Voice character limit exceeded",
                  "details": {
                    "retryable": true,
                    "retry_after_seconds": 86400,
                    "retryAfter": 86400,
                    "retryAfterSeconds": 86400
                  }
                }
              },
              "concurrentVideoJobLimit": {
                "value": {
                  "error": "concurrent_job_limit_exceeded",
                  "message": "Developer API concurrent job limit exceeded",
                  "details": {
                    "retryable": true,
                    "concurrent_jobs": 2,
                    "concurrentJobs": 2,
                    "active_jobs": 2,
                    "activeJobs": 2,
                    "available_slots": 0,
                    "availableSlots": 0,
                    "active_job_expires_in_seconds": 512,
                    "activeJobExpiresInSeconds": 512,
                    "retry_after_seconds": 512,
                    "retryAfter": 512,
                    "retryAfterSeconds": 512
                  }
                }
              }
            }
          }
        }
      },
      "UpstreamFailure": {
        "description": "Puppetry could not create or inspect the requested asset.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          },
          "X-Puppetry-Next-Poll-At": {
            "$ref": "#/components/headers/NextPollAt"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "videoQueueFailed": {
                "value": {
                  "error": "video_queue_failed",
                  "message": "Animation service did not return a video job id",
                  "details": {
                    "retryable": true,
                    "retry_after_seconds": 30,
                    "retryAfter": 30,
                    "retryAfterSeconds": 30,
                    "next_poll_at": "2026-06-22T08:50:30.000Z",
                    "nextPollAt": "2026-06-22T08:50:30.000Z"
                  }
                }
              },
              "creditDebitUnavailable": {
                "value": {
                  "error": "credit_debit_unavailable",
                  "message": "Video credit debit is temporarily unavailable",
                  "details": {
                    "retryable": true,
                    "retry_after_seconds": 30,
                    "retryAfter": 30,
                    "retryAfterSeconds": 30,
                    "next_poll_at": "2026-06-22T08:50:30.000Z",
                    "nextPollAt": "2026-06-22T08:50:30.000Z"
                  }
                }
              },
              "assetProbeUnavailable": {
                "value": {
                  "error": "asset_probe_unavailable",
                  "message": "audio_url is temporarily unavailable for inspection",
                  "details": {
                    "retryable": true,
                    "retry_after_seconds": 30,
                    "retryAfter": 30,
                    "retryAfterSeconds": 30
                  }
                }
              },
              "jobStatusUnavailable": {
                "value": {
                  "error": "job_status_unavailable",
                  "message": "Video job status is temporarily unavailable",
                  "retryable": true,
                  "retry_after_seconds": 10,
                  "retryAfter": 10,
                  "retryAfterSeconds": 10,
                  "next_poll_at": "2026-06-22T08:50:10.000Z",
                  "nextPollAt": "2026-06-22T08:50:10.000Z",
                  "credits": {
                    "charged": 0,
                    "remaining": null
                  },
                  "creation_credits": {
                    "charged": 1,
                    "remaining_after_debit": 299,
                    "remainingAfterDebit": 299
                  },
                  "creationCredits": {
                    "charged": 1,
                    "remaining_after_debit": 299,
                    "remainingAfterDebit": 299
                  },
                  "details": {
                    "retryable": true,
                    "retry_after_seconds": 10,
                    "retryAfter": 10,
                    "retryAfterSeconds": 10,
                    "next_poll_at": "2026-06-22T08:50:10.000Z",
                    "nextPollAt": "2026-06-22T08:50:10.000Z",
                    "object": "video_job",
                    "id": "api_video_abc123",
                    "job_id": "api_video_abc123",
                    "jobId": "api_video_abc123",
                    "task_id": "api_video_abc123",
                    "taskId": "api_video_abc123",
                    "status_url": "/api/v1/videos/api_video_abc123",
                    "statusUrl": "/api/v1/videos/api_video_abc123",
                    "credits": {
                      "charged": 0,
                      "remaining": null
                    },
                    "creation_credits": {
                      "charged": 1,
                      "remaining_after_debit": 299,
                      "remainingAfterDebit": 299
                    },
                    "creationCredits": {
                      "charged": 1,
                      "remaining_after_debit": 299,
                      "remainingAfterDebit": 299
                    },
                    "created_at": "2026-06-22T08:50:00.000Z",
                    "createdAt": "2026-06-22T08:50:00.000Z",
                    "content_location": "/api/v1/videos/api_video_abc123",
                    "contentLocation": "/api/v1/videos/api_video_abc123"
                  }
                }
              },
              "jobResultUnavailable": {
                "value": {
                  "error": "job_result_unavailable",
                  "message": "Video job result is temporarily unavailable",
                  "retryable": true,
                  "retry_after_seconds": 10,
                  "retryAfter": 10,
                  "retryAfterSeconds": 10,
                  "next_poll_at": "2026-06-22T08:50:10.000Z",
                  "nextPollAt": "2026-06-22T08:50:10.000Z",
                  "credits": {
                    "charged": 0,
                    "remaining": null
                  },
                  "creation_credits": {
                    "charged": 1,
                    "remaining_after_debit": 299,
                    "remainingAfterDebit": 299
                  },
                  "creationCredits": {
                    "charged": 1,
                    "remaining_after_debit": 299,
                    "remainingAfterDebit": 299
                  },
                  "details": {
                    "retryable": true,
                    "retry_after_seconds": 10,
                    "retryAfter": 10,
                    "retryAfterSeconds": 10,
                    "next_poll_at": "2026-06-22T08:50:10.000Z",
                    "nextPollAt": "2026-06-22T08:50:10.000Z",
                    "object": "video_job",
                    "id": "api_video_abc123",
                    "job_id": "api_video_abc123",
                    "jobId": "api_video_abc123",
                    "task_id": "api_video_abc123",
                    "taskId": "api_video_abc123",
                    "status_url": "/api/v1/videos/api_video_abc123",
                    "statusUrl": "/api/v1/videos/api_video_abc123",
                    "credits": {
                      "charged": 0,
                      "remaining": null
                    },
                    "creation_credits": {
                      "charged": 1,
                      "remaining_after_debit": 299,
                      "remainingAfterDebit": 299
                    },
                    "creationCredits": {
                      "charged": 1,
                      "remaining_after_debit": 299,
                      "remainingAfterDebit": 299
                    },
                    "created_at": "2026-06-22T08:50:00.000Z",
                    "createdAt": "2026-06-22T08:50:00.000Z",
                    "content_location": "/api/v1/videos/api_video_abc123",
                    "contentLocation": "/api/v1/videos/api_video_abc123"
                  }
                }
              },
              "jobFinalizationUnavailable": {
                "value": {
                  "error": "job_finalization_unavailable",
                  "message": "Video job finalization is temporarily unavailable",
                  "retryable": true,
                  "retry_after_seconds": 10,
                  "retryAfter": 10,
                  "retryAfterSeconds": 10,
                  "next_poll_at": "2026-06-22T08:50:10.000Z",
                  "nextPollAt": "2026-06-22T08:50:10.000Z",
                  "credits": {
                    "charged": 0,
                    "remaining": null
                  },
                  "creation_credits": {
                    "charged": 1,
                    "remaining_after_debit": 299,
                    "remainingAfterDebit": 299
                  },
                  "creationCredits": {
                    "charged": 1,
                    "remaining_after_debit": 299,
                    "remainingAfterDebit": 299
                  },
                  "details": {
                    "retryable": true,
                    "retry_after_seconds": 10,
                    "retryAfter": 10,
                    "retryAfterSeconds": 10,
                    "next_poll_at": "2026-06-22T08:50:10.000Z",
                    "nextPollAt": "2026-06-22T08:50:10.000Z",
                    "object": "video_job",
                    "id": "api_video_abc123",
                    "job_id": "api_video_abc123",
                    "jobId": "api_video_abc123",
                    "task_id": "api_video_abc123",
                    "taskId": "api_video_abc123",
                    "status_url": "/api/v1/videos/api_video_abc123",
                    "statusUrl": "/api/v1/videos/api_video_abc123",
                    "credits": {
                      "charged": 0,
                      "remaining": null
                    },
                    "creation_credits": {
                      "charged": 1,
                      "remaining_after_debit": 299,
                      "remainingAfterDebit": 299
                    },
                    "creationCredits": {
                      "charged": 1,
                      "remaining_after_debit": 299,
                      "remainingAfterDebit": 299
                    },
                    "created_at": "2026-06-22T08:50:00.000Z",
                    "createdAt": "2026-06-22T08:50:00.000Z",
                    "content_location": "/api/v1/videos/api_video_abc123",
                    "contentLocation": "/api/v1/videos/api_video_abc123"
                  }
                }
              }
            }
          }
        }
      },
      "StatusTimeout": {
        "description": "Puppetry timed out while checking an accepted video job.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          },
          "X-Puppetry-Next-Poll-At": {
            "$ref": "#/components/headers/NextPollAt"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "statusTimeout": {
                "value": {
                  "error": "job_status_timeout",
                  "message": "Video job status lookup timed out",
                  "retryable": true,
                  "retry_after_seconds": 10,
                  "retryAfter": 10,
                  "retryAfterSeconds": 10,
                  "next_poll_at": "2026-06-22T08:50:10.000Z",
                  "nextPollAt": "2026-06-22T08:50:10.000Z",
                  "credits": {
                    "charged": 0,
                    "remaining": null
                  },
                  "creation_credits": {
                    "charged": 1,
                    "remaining_after_debit": 299,
                    "remainingAfterDebit": 299
                  },
                  "creationCredits": {
                    "charged": 1,
                    "remaining_after_debit": 299,
                    "remainingAfterDebit": 299
                  },
                  "details": {
                    "retryable": true,
                    "retry_after_seconds": 10,
                    "retryAfter": 10,
                    "retryAfterSeconds": 10,
                    "next_poll_at": "2026-06-22T08:50:10.000Z",
                    "nextPollAt": "2026-06-22T08:50:10.000Z",
                    "object": "video_job",
                    "id": "api_video_abc123",
                    "job_id": "api_video_abc123",
                    "jobId": "api_video_abc123",
                    "task_id": "api_video_abc123",
                    "taskId": "api_video_abc123",
                    "status_url": "/api/v1/videos/api_video_abc123",
                    "statusUrl": "/api/v1/videos/api_video_abc123",
                    "credits": {
                      "charged": 0,
                      "remaining": null
                    },
                    "creation_credits": {
                      "charged": 1,
                      "remaining_after_debit": 299,
                      "remainingAfterDebit": 299
                    },
                    "creationCredits": {
                      "charged": 1,
                      "remaining_after_debit": 299,
                      "remainingAfterDebit": 299
                    },
                    "created_at": "2026-06-22T08:50:00.000Z",
                    "createdAt": "2026-06-22T08:50:00.000Z",
                    "content_location": "/api/v1/videos/api_video_abc123",
                    "contentLocation": "/api/v1/videos/api_video_abc123"
                  }
                }
              }
            }
          }
        }
      },
      "TemporarilyUnavailable": {
        "description": "A beta quota, usage lookup, or job counter is temporarily unavailable.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          },
          "X-Puppetry-Next-Poll-At": {
            "$ref": "#/components/headers/NextPollAt"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "apiAuthUnavailable": {
                "value": {
                  "error": "api_auth_unavailable",
                  "message": "Developer API authentication is temporarily unavailable",
                  "retryable": true,
                  "retry_after_seconds": 30,
                  "retryAfter": 30,
                  "retryAfterSeconds": 30,
                  "next_poll_at": "2026-06-22T08:50:30.000Z",
                  "nextPollAt": "2026-06-22T08:50:30.000Z",
                  "details": {
                    "retryable": true,
                    "retry_after_seconds": 30,
                    "retryAfter": 30,
                    "retryAfterSeconds": 30,
                    "next_poll_at": "2026-06-22T08:50:30.000Z",
                    "nextPollAt": "2026-06-22T08:50:30.000Z"
                  }
                }
              }
            }
          }
        }
      },
      "InsufficientCredits": {
        "description": "The account does not have enough video credits to create the job.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "insufficientCredits": {
                "value": {
                  "error": "insufficient_credits",
                  "message": "Not enough video credits to create a video job",
                  "details": {
                    "code": "insufficient_video_credits",
                    "reason": "insufficient_video_credits",
                    "required_credits": 1,
                    "requiredCredits": 1,
                    "remaining_credits": 0,
                    "remainingCredits": 0,
                    "retryable": false,
                    "retry_blocked_reason": "Developer API video generation needs 1 credit, but only 0 remain.",
                    "retryBlockedReason": "Developer API video generation needs 1 credit, but only 0 remain.",
                    "retry_block_code": "INSUFFICIENT_VIDEO_CREDITS",
                    "retryBlockCode": "INSUFFICIENT_VIDEO_CREDITS",
                    "checkout_url": "https://www.puppetry.com/pricing#pricing-credit-packs",
                    "checkoutUrl": "https://www.puppetry.com/pricing#pricing-credit-packs",
                    "top_up": "Buy 5 video credits for $4.99 at https://www.puppetry.com/pricing#pricing-credit-packs",
                    "topUp": "Buy 5 video credits for $4.99 at https://www.puppetry.com/pricing#pricing-credit-packs",
                    "video_generation": {
                      "can_create": false,
                      "canCreate": false,
                      "required_credits": 1,
                      "requiredCredits": 1,
                      "credits_remaining": 0,
                      "creditsRemaining": 0,
                      "concurrent_jobs": 2,
                      "concurrentJobs": 2,
                      "active_jobs": 0,
                      "activeJobs": 0,
                      "available_slots": 2,
                      "availableSlots": 2,
                      "reason": "insufficient_video_credits"
                    },
                    "videoGeneration": {
                      "can_create": false,
                      "canCreate": false,
                      "required_credits": 1,
                      "requiredCredits": 1,
                      "credits_remaining": 0,
                      "creditsRemaining": 0,
                      "concurrent_jobs": 2,
                      "concurrentJobs": 2,
                      "active_jobs": 0,
                      "activeJobs": 0,
                      "available_slots": 2,
                      "availableSlots": 2,
                      "reason": "insufficient_video_credits"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "IdempotencyConflict": {
        "description": "The Idempotency-Key is already creating a video job or hosted upload URL, or it was reused with different input. Video create conflicts include Location/Content-Location, Puppetry-Operation-Id, and Puppetry-Request-Id headers for the existing API video job when one is known.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          },
          "X-Puppetry-Next-Poll-At": {
            "$ref": "#/components/headers/NextPollAt"
          },
          "Location": {
            "$ref": "#/components/headers/VideoJobStatusLocation"
          },
          "Content-Location": {
            "$ref": "#/components/headers/VideoJobStatusLocation"
          },
          "Puppetry-Operation-Id": {
            "$ref": "#/components/headers/VideoJobOperationId"
          },
          "X-Puppetry-Operation-Id": {
            "$ref": "#/components/headers/VideoJobOperationId"
          },
          "Puppetry-Request-Id": {
            "$ref": "#/components/headers/VideoJobRequestId"
          },
          "X-Puppetry-Request-Id": {
            "$ref": "#/components/headers/VideoJobRequestId"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "idempotencyInProgress": {
                "value": {
                  "error": "idempotency_key_in_progress",
                  "message": "A video job or upload URL with this Idempotency-Key is already being created. Retry this request shortly.",
                  "details": {
                    "retryable": true,
                    "retry_after_seconds": 5,
                    "retryAfter": 5,
                    "retryAfterSeconds": 5,
                    "next_poll_at": "2026-06-22T08:50:05.000Z",
                    "nextPollAt": "2026-06-22T08:50:05.000Z"
                  }
                }
              },
              "idempotencyKeyConflict": {
                "value": {
                  "error": "idempotency_key_conflict",
                  "message": "Idempotency-Key was already used with a different video create request",
                  "object": "video_job",
                  "id": "api_video_existing",
                  "job_id": "api_video_existing",
                  "jobId": "api_video_existing",
                  "operation_id": "api_video_existing",
                  "operationId": "api_video_existing",
                  "request_id": "api_video_existing",
                  "requestId": "api_video_existing",
                  "source": "text",
                  "request_source": "text",
                  "requestSource": "text",
                  "status_url": "/api/v1/videos/api_video_existing",
                  "statusUrl": "/api/v1/videos/api_video_existing",
                  "content_location": "/api/v1/videos/api_video_existing",
                  "contentLocation": "/api/v1/videos/api_video_existing",
                  "details": {
                    "retryable": false,
                    "retry_blocked_reason": "Idempotency-Key was already used with a different video create request",
                    "retryBlockedReason": "Idempotency-Key was already used with a different video create request",
                    "retry_block_code": "IDEMPOTENCY_KEY_CONFLICT",
                    "retryBlockCode": "IDEMPOTENCY_KEY_CONFLICT",
                    "object": "video_job",
                    "id": "api_video_existing",
                    "status_url": "/api/v1/videos/api_video_existing",
                    "statusUrl": "/api/v1/videos/api_video_existing",
                    "content_location": "/api/v1/videos/api_video_existing",
                    "contentLocation": "/api/v1/videos/api_video_existing"
                  }
                }
              }
            }
          }
        }
      },
      "VideoJobStatusUnavailable": {
        "description": "Puppetry could not inspect an accepted video job. Retry-After, X-Puppetry-Next-Poll-At, Location, Content-Location, Puppetry-Operation-Id, and Puppetry-Request-Id identify and pace the same status poll target.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          },
          "X-Puppetry-Next-Poll-At": {
            "$ref": "#/components/headers/NextPollAt"
          },
          "Location": {
            "$ref": "#/components/headers/VideoJobStatusLocation"
          },
          "Content-Location": {
            "$ref": "#/components/headers/VideoJobStatusLocation"
          },
          "Puppetry-Operation-Id": {
            "$ref": "#/components/headers/VideoJobOperationId"
          },
          "X-Puppetry-Operation-Id": {
            "$ref": "#/components/headers/VideoJobOperationId"
          },
          "Puppetry-Request-Id": {
            "$ref": "#/components/headers/VideoJobRequestId"
          },
          "X-Puppetry-Request-Id": {
            "$ref": "#/components/headers/VideoJobRequestId"
          },
          "X-Puppetry-Credits-Required": {
            "$ref": "#/components/headers/VideoJobCreditsRequired"
          },
          "X-Puppetry-Credits-Remaining": {
            "$ref": "#/components/headers/VideoJobCreditsRemaining"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "jobStatusUnavailable": {
                "value": {
                  "error": "job_status_unavailable",
                  "message": "Video job status is temporarily unavailable",
                  "retryable": true,
                  "retry_after_seconds": 10,
                  "retryAfter": 10,
                  "retryAfterSeconds": 10,
                  "next_poll_at": "2026-06-22T08:50:10.000Z",
                  "nextPollAt": "2026-06-22T08:50:10.000Z",
                  "credits": {
                    "charged": 0,
                    "remaining": null
                  },
                  "creation_credits": {
                    "charged": 1,
                    "remaining_after_debit": 299,
                    "remainingAfterDebit": 299
                  },
                  "creationCredits": {
                    "charged": 1,
                    "remaining_after_debit": 299,
                    "remainingAfterDebit": 299
                  },
                  "details": {
                    "retryable": true,
                    "retry_after_seconds": 10,
                    "retryAfter": 10,
                    "retryAfterSeconds": 10,
                    "next_poll_at": "2026-06-22T08:50:10.000Z",
                    "nextPollAt": "2026-06-22T08:50:10.000Z",
                    "object": "video_job",
                    "id": "api_video_abc123",
                    "job_id": "api_video_abc123",
                    "jobId": "api_video_abc123",
                    "task_id": "api_video_abc123",
                    "taskId": "api_video_abc123",
                    "status_url": "/api/v1/videos/api_video_abc123",
                    "statusUrl": "/api/v1/videos/api_video_abc123",
                    "credits": {
                      "charged": 0,
                      "remaining": null
                    },
                    "creation_credits": {
                      "charged": 1,
                      "remaining_after_debit": 299,
                      "remainingAfterDebit": 299
                    },
                    "creationCredits": {
                      "charged": 1,
                      "remaining_after_debit": 299,
                      "remainingAfterDebit": 299
                    },
                    "created_at": "2026-06-22T08:50:00.000Z",
                    "createdAt": "2026-06-22T08:50:00.000Z",
                    "content_location": "/api/v1/videos/api_video_abc123",
                    "contentLocation": "/api/v1/videos/api_video_abc123"
                  }
                }
              },
              "jobResultUnavailable": {
                "value": {
                  "error": "job_result_unavailable",
                  "message": "Video job result is temporarily unavailable",
                  "retryable": true,
                  "retry_after_seconds": 10,
                  "retryAfter": 10,
                  "retryAfterSeconds": 10,
                  "next_poll_at": "2026-06-22T08:50:10.000Z",
                  "nextPollAt": "2026-06-22T08:50:10.000Z",
                  "credits": {
                    "charged": 0,
                    "remaining": null
                  },
                  "creation_credits": {
                    "charged": 1,
                    "remaining_after_debit": 299,
                    "remainingAfterDebit": 299
                  },
                  "creationCredits": {
                    "charged": 1,
                    "remaining_after_debit": 299,
                    "remainingAfterDebit": 299
                  },
                  "details": {
                    "retryable": true,
                    "retry_after_seconds": 10,
                    "retryAfter": 10,
                    "retryAfterSeconds": 10,
                    "next_poll_at": "2026-06-22T08:50:10.000Z",
                    "nextPollAt": "2026-06-22T08:50:10.000Z",
                    "object": "video_job",
                    "id": "api_video_abc123",
                    "job_id": "api_video_abc123",
                    "jobId": "api_video_abc123",
                    "task_id": "api_video_abc123",
                    "taskId": "api_video_abc123",
                    "status_url": "/api/v1/videos/api_video_abc123",
                    "statusUrl": "/api/v1/videos/api_video_abc123",
                    "credits": {
                      "charged": 0,
                      "remaining": null
                    },
                    "creation_credits": {
                      "charged": 1,
                      "remaining_after_debit": 299,
                      "remainingAfterDebit": 299
                    },
                    "creationCredits": {
                      "charged": 1,
                      "remaining_after_debit": 299,
                      "remainingAfterDebit": 299
                    },
                    "created_at": "2026-06-22T08:50:00.000Z",
                    "createdAt": "2026-06-22T08:50:00.000Z",
                    "content_location": "/api/v1/videos/api_video_abc123",
                    "contentLocation": "/api/v1/videos/api_video_abc123"
                  }
                }
              }
            }
          }
        }
      },
      "VideoJobStatusTimeout": {
        "description": "Puppetry timed out while checking an accepted video job. Retry-After, X-Puppetry-Next-Poll-At, Location, Content-Location, Puppetry-Operation-Id, and Puppetry-Request-Id identify and pace the same status poll target.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          },
          "X-Puppetry-Next-Poll-At": {
            "$ref": "#/components/headers/NextPollAt"
          },
          "Location": {
            "$ref": "#/components/headers/VideoJobStatusLocation"
          },
          "Content-Location": {
            "$ref": "#/components/headers/VideoJobStatusLocation"
          },
          "Puppetry-Operation-Id": {
            "$ref": "#/components/headers/VideoJobOperationId"
          },
          "X-Puppetry-Operation-Id": {
            "$ref": "#/components/headers/VideoJobOperationId"
          },
          "Puppetry-Request-Id": {
            "$ref": "#/components/headers/VideoJobRequestId"
          },
          "X-Puppetry-Request-Id": {
            "$ref": "#/components/headers/VideoJobRequestId"
          },
          "X-Puppetry-Credits-Required": {
            "$ref": "#/components/headers/VideoJobCreditsRequired"
          },
          "X-Puppetry-Credits-Remaining": {
            "$ref": "#/components/headers/VideoJobCreditsRemaining"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "statusTimeout": {
                "value": {
                  "error": "job_status_timeout",
                  "message": "Video job status lookup timed out",
                  "retryable": true,
                  "retry_after_seconds": 10,
                  "retryAfter": 10,
                  "retryAfterSeconds": 10,
                  "next_poll_at": "2026-06-22T08:50:10.000Z",
                  "nextPollAt": "2026-06-22T08:50:10.000Z",
                  "credits": {
                    "charged": 0,
                    "remaining": null
                  },
                  "creation_credits": {
                    "charged": 1,
                    "remaining_after_debit": 299,
                    "remainingAfterDebit": 299
                  },
                  "creationCredits": {
                    "charged": 1,
                    "remaining_after_debit": 299,
                    "remainingAfterDebit": 299
                  },
                  "details": {
                    "retryable": true,
                    "retry_after_seconds": 10,
                    "retryAfter": 10,
                    "retryAfterSeconds": 10,
                    "next_poll_at": "2026-06-22T08:50:10.000Z",
                    "nextPollAt": "2026-06-22T08:50:10.000Z",
                    "object": "video_job",
                    "id": "api_video_abc123",
                    "job_id": "api_video_abc123",
                    "jobId": "api_video_abc123",
                    "task_id": "api_video_abc123",
                    "taskId": "api_video_abc123",
                    "status_url": "/api/v1/videos/api_video_abc123",
                    "statusUrl": "/api/v1/videos/api_video_abc123",
                    "credits": {
                      "charged": 0,
                      "remaining": null
                    },
                    "creation_credits": {
                      "charged": 1,
                      "remaining_after_debit": 299,
                      "remainingAfterDebit": 299
                    },
                    "creationCredits": {
                      "charged": 1,
                      "remaining_after_debit": 299,
                      "remainingAfterDebit": 299
                    },
                    "created_at": "2026-06-22T08:50:00.000Z",
                    "createdAt": "2026-06-22T08:50:00.000Z",
                    "content_location": "/api/v1/videos/api_video_abc123",
                    "contentLocation": "/api/v1/videos/api_video_abc123"
                  }
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "ErrorResponse": {
        "type": "object",
        "required": ["error", "message"],
        "properties": {
          "error": {
            "type": "string",
            "examples": ["invalid_request", "unauthorized", "rate_limited"]
          },
          "message": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "const": "video_job",
            "description": "Present on retryable transient video-status errors when the affected API video job is known."
          },
          "id": {
            "type": "string",
            "description": "Present on retryable transient video-status errors. API video job ID to keep polling."
          },
          "credits": {
            "type": "object",
            "description": "Present on retryable transient video-status errors for accepted API video jobs. Status polls are never billed, so charged is 0.",
            "properties": {
              "charged": {
                "type": "integer",
                "minimum": 0,
                "example": 0
              },
              "remaining": {
                "type": ["integer", "null"],
                "minimum": 0,
                "description": "Current balance if available; null when the status poll did not fetch credit state."
              }
            },
            "additionalProperties": false
          },
          "creation_credits": {
            "$ref": "#/components/schemas/VideoJobCreationCredits",
            "description": "Original create-time video credit debit evidence for the affected API video job when available."
          },
          "creationCredits": {
            "$ref": "#/components/schemas/VideoJobCreationCredits",
            "description": "SDK-style alias for creation_credits."
          },
          "retryable": {
            "type": "boolean",
            "description": "Present on retryable transient errors and non-retryable terminal video-create/status errors so generated clients can identify retry/backoff behavior without parsing details."
          },
          "retry_blocked_reason": {
            "type": "string",
            "description": "Present on non-retryable terminal video-create/status errors. Redacted reason explaining why clients should not retry the same request."
          },
          "retryBlockedReason": {
            "type": "string",
            "description": "SDK-style alias for retry_blocked_reason."
          },
          "retry_block_code": {
            "type": "string",
            "description": "Present on non-retryable terminal video-create/status errors. Stable retry-block code suitable for generated clients."
          },
          "retryBlockCode": {
            "type": "string",
            "description": "SDK-style alias for retry_block_code."
          },
          "retry_after_seconds": {
            "type": "integer",
            "minimum": 0,
            "description": "Present on retryable transient errors. Mirrors Retry-After and details.retry_after_seconds."
          },
          "retryAfter": {
            "type": "integer",
            "minimum": 0,
            "description": "SDK-style alias for retry_after_seconds."
          },
          "retryAfterSeconds": {
            "type": "integer",
            "minimum": 0,
            "description": "CamelCase alias for retry_after_seconds."
          },
          "next_poll_at": {
            "type": "string",
            "format": "date-time",
            "description": "Present on retryable transient errors. Absolute UTC time when clients should retry the request or poll again."
          },
          "nextPollAt": {
            "type": "string",
            "format": "date-time",
            "description": "SDK-style alias for next_poll_at."
          },
          "job_id": {
            "type": "string",
            "description": "Present on retryable transient video-status errors so clients can correlate the retry to the API video job."
          },
          "jobId": {
            "type": "string",
            "description": "SDK-style alias for job_id."
          },
          "task_id": {
            "type": "string",
            "description": "Task-style alias for job_id on retryable transient video-status errors."
          },
          "taskId": {
            "type": "string",
            "description": "CamelCase task-style alias for job_id."
          },
          "source": {
            "type": "string",
            "enum": ["text", "audio"],
            "description": "Video creation source for the affected API video job."
          },
          "request_source": {
            "type": "string",
            "enum": ["text", "audio"],
            "description": "Alias for source."
          },
          "requestSource": {
            "type": "string",
            "enum": ["text", "audio"],
            "description": "SDK-style alias for source."
          },
          "status_url": {
            "type": "string",
            "description": "Present on retryable transient video-status errors so clients know which job resource to poll next."
          },
          "statusUrl": {
            "type": "string",
            "description": "SDK-style alias for status_url."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Creation time for the affected API video job."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "SDK-style alias for created_at."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the affected API video job record expires from status lookup."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "SDK-style alias for expires_at."
          },
          "details": {
            "description": "Endpoint-specific diagnostic fields.",
            "type": "object",
            "additionalProperties": true
          },
          "content_location": {
            "type": "string",
            "description": "Body alias for the status poll URL, mirroring the Content-Location response header target for clients that cannot reliably read headers."
          },
          "contentLocation": {
            "type": "string",
            "description": "SDK-style alias for content_location."
          }
        },
        "additionalProperties": true
      },
      "PuppetryVoice": {
        "type": "object",
        "required": [
          "id",
          "object",
          "name",
          "provider",
          "language",
          "language_code",
          "gender",
          "description",
          "styles",
          "preview_url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Public Puppetry voice ID.",
            "example": "puppetry-af_bella"
          },
          "object": {
            "type": "string",
            "const": "voice"
          },
          "name": {
            "type": "string",
            "example": "Bella"
          },
          "provider": {
            "type": "string",
            "const": "puppetry"
          },
          "language": {
            "type": "string",
            "example": "English"
          },
          "language_code": {
            "type": "string",
            "example": "en"
          },
          "gender": {
            "type": "string",
            "example": "Female"
          },
          "description": {
            "type": "string",
            "example": "Puppetry voice (English)"
          },
          "styles": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "preview_url": {
            "type": "string",
            "format": "uri-reference",
            "description": "Fixed audio preview path for this Puppetry voice.",
            "example": "/api/voices/puppetry-preview?voice=af_bella&language=en"
          }
        },
        "additionalProperties": false
      },
      "VoiceListResponse": {
        "type": "object",
        "required": ["object", "data"],
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PuppetryVoice"
            }
          }
        },
        "additionalProperties": false
      },
      "TtsRequest": {
        "type": "object",
        "required": ["voice_id", "text"],
        "properties": {
          "voice_id": {
            "type": "string",
            "description": "Public voice ID from GET /api/v1/voices/puppetry.",
            "example": "puppetry-af_bella"
          },
          "text": {
            "type": "string",
            "minLength": 1,
            "maxLength": 5000,
            "description": "Text to synthesize.",
            "example": "Hello from Puppetry."
          },
          "speed": {
            "type": "number",
            "minimum": 0.5,
            "maximum": 2,
            "default": 1,
            "description": "Speech speed multiplier."
          }
        },
        "additionalProperties": false
      },
      "TtsResponse": {
        "type": "object",
        "required": [
          "object",
          "id",
          "voice_id",
          "audio_url",
          "format",
          "usage"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "audio"
          },
          "id": {
            "type": "string",
            "description": "Generated audio asset ID."
          },
          "voice_id": {
            "type": "string",
            "example": "puppetry-af_bella"
          },
          "audio_url": {
            "type": "string",
            "format": "uri",
            "description": "Hosted URL for the generated WAV audio."
          },
          "format": {
            "type": "string",
            "const": "wav"
          },
          "usage": {
            "type": "object",
            "required": ["voice_characters"],
            "properties": {
              "voice_characters": {
                "type": "object",
                "required": ["used", "limit", "remaining"],
                "properties": {
                  "used": {
                    "type": "integer",
                    "example": 20
                  },
                  "limit": {
                    "type": "integer",
                    "example": 100000
                  },
                  "remaining": {
                    "type": "integer",
                    "example": 99980
                  }
                },
                "additionalProperties": false
              }
            },
            "additionalProperties": false
          }
        },
        "additionalProperties": false
      },
      "AudioUploadRequest": {
        "type": "object",
        "description": "Reserve a hosted audio upload URL. Pass one MIME alias (mime_type, mimeType, or contentType), one byte-count alias (content_length, contentLength, sizeBytes, file_size, or fileSize), and optionally one retry-key alias (Idempotency-Key header, idempotencyKey, or idempotency_key). Audio-to-video callers can send require_video_readiness/requireVideoReadiness=true to require current video credits and active-job capacity before reserving upload quota.",
        "anyOf": [
          {
            "required": ["mime_type", "content_length"]
          },
          {
            "required": ["mime_type", "contentLength"]
          },
          {
            "required": ["mime_type", "sizeBytes"]
          },
          {
            "required": ["mime_type", "file_size"]
          },
          {
            "required": ["mime_type", "fileSize"]
          },
          {
            "required": ["mimeType", "content_length"]
          },
          {
            "required": ["mimeType", "contentLength"]
          },
          {
            "required": ["mimeType", "sizeBytes"]
          },
          {
            "required": ["mimeType", "file_size"]
          },
          {
            "required": ["mimeType", "fileSize"]
          },
          {
            "required": ["contentType", "content_length"]
          },
          {
            "required": ["contentType", "contentLength"]
          },
          {
            "required": ["contentType", "sizeBytes"]
          },
          {
            "required": ["contentType", "file_size"]
          },
          {
            "required": ["contentType", "fileSize"]
          }
        ],
        "properties": {
          "mime_type": {
            "type": "string",
            "description": "Supported audio MIME type.",
            "enum": [
              "audio/wav",
              "audio/x-wav",
              "audio/mpeg",
              "audio/mp3",
              "audio/mp4",
              "audio/m4a",
              "audio/x-m4a"
            ],
            "example": "audio/mpeg"
          },
          "mimeType": {
            "type": "string",
            "description": "CamelCase alias for mime_type.",
            "enum": [
              "audio/wav",
              "audio/x-wav",
              "audio/mpeg",
              "audio/mp3",
              "audio/mp4",
              "audio/m4a",
              "audio/x-m4a"
            ],
            "example": "audio/mpeg"
          },
          "contentType": {
            "type": "string",
            "description": "Agent-friendly alias for mime_type.",
            "enum": [
              "audio/wav",
              "audio/x-wav",
              "audio/mpeg",
              "audio/mp3",
              "audio/mp4",
              "audio/m4a",
              "audio/x-m4a"
            ],
            "example": "audio/mpeg"
          },
          "content_length": {
            "type": "integer",
            "minimum": 1,
            "maximum": 52428800,
            "description": "Audio file size in bytes.",
            "example": 1048576
          },
          "contentLength": {
            "type": "integer",
            "minimum": 1,
            "maximum": 52428800,
            "description": "CamelCase alias for content_length.",
            "example": 1048576
          },
          "sizeBytes": {
            "type": "integer",
            "minimum": 1,
            "maximum": 52428800,
            "description": "Agent/runtime alias for content_length.",
            "example": 1048576
          },
          "file_size": {
            "type": "integer",
            "minimum": 1,
            "maximum": 52428800,
            "description": "Upload metadata alias for content_length.",
            "example": 1048576
          },
          "fileSize": {
            "type": "integer",
            "minimum": 1,
            "maximum": 52428800,
            "description": "Upload metadata alias for content_length.",
            "example": 1048576
          },
          "idempotencyKey": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Optional body alias for the Idempotency-Key retry header when custom headers are unavailable.",
            "example": "upload-demo-1"
          },
          "idempotency_key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Snake_case body alias for Idempotency-Key.",
            "example": "upload-demo-1"
          },
          "require_video_readiness": {
            "type": "boolean",
            "description": "When true, the route verifies video_generation readiness before creating a new upload reservation and returns 402/429 with video_generation details if credits or active slots are blocked.",
            "example": true
          },
          "requireVideoReadiness": {
            "type": "boolean",
            "description": "CamelCase alias for require_video_readiness.",
            "example": true
          },
          "video_readiness_required": {
            "type": "boolean",
            "description": "Snake_case compatibility alias for require_video_readiness.",
            "example": true
          },
          "videoReadinessRequired": {
            "type": "boolean",
            "description": "CamelCase compatibility alias for require_video_readiness.",
            "example": true
          }
        },
        "additionalProperties": false
      },
      "AudioUploadUrlResponse": {
        "type": "object",
        "required": [
          "object",
          "id",
          "upload_url",
          "uploadUrl",
          "method",
          "headers",
          "key",
          "read_url",
          "readUrl",
          "read_url_expires_in",
          "readUrlExpiresIn",
          "read_url_expires_at",
          "readUrlExpiresAt",
          "upload_url_expires_in",
          "uploadUrlExpiresIn",
          "limits"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "upload_url"
          },
          "id": {
            "type": "string",
            "example": "apiupl_2f4f3f1b2e9e4f63a20a0b548d4b7a8c"
          },
          "upload_url": {
            "type": "string",
            "format": "uri",
            "description": "Short-lived signed PUT URL."
          },
          "uploadUrl": {
            "type": "string",
            "format": "uri",
            "description": "Camel-case alias of upload_url for SDK and agent callers."
          },
          "method": {
            "type": "string",
            "const": "PUT"
          },
          "headers": {
            "type": "object",
            "required": ["Content-Type", "Content-Length"],
            "properties": {
              "Content-Type": {
                "type": "string",
                "example": "audio/mpeg"
              },
              "Content-Length": {
                "type": "string",
                "example": "1048576"
              }
            },
            "additionalProperties": false
          },
          "key": {
            "type": "string",
            "description": "S3 object key for the hosted upload. Puppetry-hosted API upload keys are scoped to the API key workspace."
          },
          "read_url": {
            "type": "string",
            "format": "uri",
            "description": "Signed URL clients can use after uploading the file. It stays valid long enough for queued video jobs to start. When used as audio_url for /api/v1/videos/audio, the hosted upload must belong to the same API key that queues the video job; Puppetry re-signs the stored object key before validation and queueing."
          },
          "readUrl": {
            "type": "string",
            "format": "uri",
            "description": "Camel-case alias of read_url for SDK and agent callers."
          },
          "read_url_expires_in": {
            "type": "integer",
            "example": 86400
          },
          "readUrlExpiresIn": {
            "type": "integer",
            "example": 86400,
            "description": "Camel-case alias of read_url_expires_in."
          },
          "read_url_expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "readUrlExpiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Camel-case alias of read_url_expires_at."
          },
          "upload_url_expires_in": {
            "type": "integer",
            "example": 3600
          },
          "uploadUrlExpiresIn": {
            "type": "integer",
            "example": 3600,
            "description": "Camel-case alias of upload_url_expires_in."
          },
          "idempotent_replay": {
            "type": "boolean",
            "description": "Present and true when an Idempotency-Key replay returns an existing pending upload reservation without reserving duplicate quota."
          },
          "idempotentReplay": {
            "type": "boolean",
            "description": "Camel-case alias of idempotent_replay."
          },
          "limits": {
            "type": "object",
            "required": [
              "max_file_bytes",
              "maxFileBytes",
              "monthly_uploads",
              "monthlyUploads",
              "storage_bytes",
              "storageBytes",
              "remaining_monthly_uploads",
              "remainingMonthlyUploads",
              "remaining_storage_bytes",
              "remainingStorageBytes"
            ],
            "properties": {
              "max_file_bytes": {
                "type": "integer",
                "example": 52428800
              },
              "maxFileBytes": {
                "type": "integer",
                "example": 52428800,
                "description": "Camel-case alias of max_file_bytes."
              },
              "monthly_uploads": {
                "type": "integer",
                "example": 250
              },
              "monthlyUploads": {
                "type": "integer",
                "example": 250,
                "description": "Camel-case alias of monthly_uploads."
              },
              "storage_bytes": {
                "type": "integer",
                "example": 2147483648
              },
              "storageBytes": {
                "type": "integer",
                "example": 2147483648,
                "description": "Camel-case alias of storage_bytes."
              },
              "remaining_monthly_uploads": {
                "type": "integer",
                "example": 249
              },
              "remainingMonthlyUploads": {
                "type": "integer",
                "example": 249,
                "description": "Camel-case alias of remaining_monthly_uploads."
              },
              "remaining_storage_bytes": {
                "type": "integer",
                "example": 2146435072
              },
              "remainingStorageBytes": {
                "type": "integer",
                "example": 2146435072,
                "description": "Camel-case alias of remaining_storage_bytes."
              }
            },
            "additionalProperties": false
          }
        },
        "additionalProperties": false
      },
      "TextVideoRequest": {
        "type": "object",
        "allOf": [
          {
            "anyOf": [
              {
                "required": ["image_url"]
              },
              {
                "required": ["imageUrl"]
              },
              {
                "required": ["image"]
              },
              {
                "required": ["portrait"]
              },
              {
                "required": ["portrait_url"]
              },
              {
                "required": ["portraitUrl"]
              },
              {
                "required": ["avatar"]
              },
              {
                "required": ["avatarUrl"]
              },
              {
                "required": ["avatar_url"]
              },
              {
                "required": ["photo"]
              },
              {
                "required": ["photoUrl"]
              },
              {
                "required": ["photo_url"]
              }
            ]
          },
          {
            "anyOf": [
              {
                "required": ["text"]
              },
              {
                "required": ["script"]
              },
              {
                "required": ["prompt"]
              }
            ]
          }
        ],
        "properties": {
          "image_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "HTTPS image URL for the portrait or puppet source.",
            "example": "https://example.com/portrait.png"
          },
          "imageUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "CamelCase alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "image": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Agent-friendly alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "portrait": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Portrait image alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "portrait_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Snake_case portrait alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "portraitUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "CamelCase portrait alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "avatar": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Avatar image alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "avatarUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "CamelCase avatar alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "avatar_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Snake_case avatar alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "photo": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Photo image alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "photoUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "CamelCase photo alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "photo_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Snake_case photo alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "text": {
            "type": "string",
            "minLength": 1,
            "maxLength": 10000,
            "description": "Script text to speak in the generated video.",
            "example": "Hello from the Puppetry video API."
          },
          "script": {
            "type": "string",
            "minLength": 1,
            "maxLength": 10000,
            "description": "Agent-friendly alias for text.",
            "example": "Hello from the Puppetry video API."
          },
          "prompt": {
            "type": "string",
            "minLength": 1,
            "maxLength": 10000,
            "description": "Agent-friendly alias for text.",
            "example": "Hello from the Puppetry video API."
          },
          "voice_id": {
            "type": "string",
            "description": "Public Puppetry voice ID from GET /api/v1/voices/puppetry. Defaults to the Puppetry default voice.",
            "example": "puppetry-af_heart"
          },
          "voiceId": {
            "type": "string",
            "description": "CamelCase alias for voice_id.",
            "example": "puppetry-af_heart"
          },
          "voice": {
            "type": "string",
            "description": "Agent-friendly alias for voice_id.",
            "example": "puppetry-af_heart"
          },
          "language": {
            "type": "string",
            "minLength": 2,
            "maxLength": 16,
            "default": "en",
            "example": "en"
          },
          "expressiveness": {
            "type": "number",
            "minimum": 0,
            "maximum": 2,
            "default": 1,
            "description": "LivePortrait expression scale for the generated talking-head motion."
          },
          "idempotencyKey": {
            "type": "string",
            "maxLength": 200,
            "description": "Optional request-body alias for the Idempotency-Key header. If both header and body aliases are provided, they must match.",
            "example": "text-video-demo-1"
          },
          "idempotency_key": {
            "type": "string",
            "maxLength": 200,
            "description": "Snake_case request-body alias for Idempotency-Key. If both body aliases are provided, they must match.",
            "example": "text-video-demo-1"
          }
        },
        "additionalProperties": false
      },
      "AudioVideoRequest": {
        "type": "object",
        "allOf": [
          {
            "anyOf": [
              {
                "required": ["image_url"]
              },
              {
                "required": ["imageUrl"]
              },
              {
                "required": ["image"]
              },
              {
                "required": ["portrait"]
              },
              {
                "required": ["portrait_url"]
              },
              {
                "required": ["portraitUrl"]
              },
              {
                "required": ["avatar"]
              },
              {
                "required": ["avatarUrl"]
              },
              {
                "required": ["avatar_url"]
              },
              {
                "required": ["photo"]
              },
              {
                "required": ["photoUrl"]
              },
              {
                "required": ["photo_url"]
              }
            ]
          },
          {
            "anyOf": [
              {
                "required": ["audio_url"]
              },
              {
                "required": ["audioUrl"]
              },
              {
                "required": ["audio"]
              }
            ]
          }
        ],
        "properties": {
          "image_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "HTTPS image URL for the portrait or puppet source.",
            "example": "https://example.com/portrait.png"
          },
          "imageUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "CamelCase alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "image": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Agent-friendly alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "portrait": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Portrait image alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "portrait_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Snake_case portrait alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "portraitUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "CamelCase portrait alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "avatar": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Avatar image alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "avatarUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "CamelCase avatar alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "avatar_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Snake_case avatar alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "photo": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Photo image alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "photoUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "CamelCase photo alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "photo_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Snake_case photo alias for image_url.",
            "example": "https://example.com/portrait.png"
          },
          "audio_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "HTTPS audio URL. Use /api/v1/uploads/audio-url when the audio is not already hosted; Puppetry-hosted API uploads must belong to the same API key that queues the video job. Rotated or expired signed read URL query params for the same hosted upload are treated as the same idempotent create input, and the stored key is re-signed before validation and queueing.",
            "example": "https://assets.puppetry.com/api-uploads/audio.mp3"
          },
          "audioUrl": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "CamelCase alias for audio_url.",
            "example": "https://assets.puppetry.com/api-uploads/audio.mp3"
          },
          "audio": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "Agent-friendly alias for audio_url.",
            "example": "https://assets.puppetry.com/api-uploads/audio.mp3"
          },
          "expressiveness": {
            "type": "number",
            "minimum": 0,
            "maximum": 2,
            "default": 1,
            "description": "LivePortrait expression scale for the generated talking-head motion."
          },
          "idempotencyKey": {
            "type": "string",
            "maxLength": 200,
            "description": "Optional request-body alias for the Idempotency-Key header. If both header and body aliases are provided, they must match.",
            "example": "audio-video-demo-1"
          },
          "idempotency_key": {
            "type": "string",
            "maxLength": 200,
            "description": "Snake_case request-body alias for Idempotency-Key. If both body aliases are provided, they must match.",
            "example": "audio-video-demo-1"
          }
        },
        "additionalProperties": false
      },
      "VideoCredits": {
        "type": "object",
        "required": ["charged", "remaining"],
        "properties": {
          "charged": {
            "type": "integer",
            "example": 1
          },
          "remaining": {
            "type": "integer",
            "nullable": true,
            "example": 299
          }
        },
        "additionalProperties": false
      },
      "UsageApiKeyVideoCredits": {
        "type": "object",
        "required": ["used", "refunded", "net_used", "transactions"],
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque API key ID for the authenticated key."
          },
          "key_id": {
            "type": "string",
            "description": "Snake-case alias for id."
          },
          "keyId": {
            "type": "string",
            "description": "CamelCase alias for key_id."
          },
          "name": {
            "type": "string",
            "description": "User-visible API key name, if available."
          },
          "key_prefix": {
            "type": "string",
            "description": "Stored display prefix for the API key. This is not the secret key."
          },
          "keyPrefix": {
            "type": "string",
            "description": "CamelCase alias for key_prefix."
          },
          "type": {
            "type": "string",
            "enum": ["developer_api", "mcp"],
            "description": "Key family used for the current request."
          },
          "used": {
            "type": "integer",
            "minimum": 0,
            "description": "Video credits charged to this API key during the current UTC month."
          },
          "refunded": {
            "type": "integer",
            "minimum": 0,
            "description": "Video credits refunded to this API key during the current UTC month."
          },
          "net_used": {
            "type": "integer",
            "description": "Monthly used credits minus refunded credits for this API key."
          },
          "netUsed": {
            "type": "integer",
            "description": "CamelCase alias for net_used."
          },
          "transactions": {
            "type": "integer",
            "minimum": 0,
            "description": "Credit usage/refund transactions counted for this API key during the current UTC month."
          }
        },
        "additionalProperties": false
      },
      "UsageVideoCredits": {
        "type": "object",
        "required": [
          "balance",
          "month",
          "reset_at",
          "used",
          "refunded",
          "net_used",
          "transactions",
          "current_api_key"
        ],
        "properties": {
          "balance": {
            "type": "integer",
            "description": "Current video credit balance for the authenticated account."
          },
          "month": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}$",
            "description": "UTC usage month represented by the counters."
          },
          "reset_at": {
            "type": "string",
            "format": "date-time",
            "description": "UTC time when the monthly usage window resets."
          },
          "resetAt": {
            "type": "string",
            "format": "date-time",
            "description": "CamelCase alias for reset_at."
          },
          "used": {
            "type": "integer",
            "minimum": 0,
            "description": "Video credits charged to the authenticated workspace during the current UTC month."
          },
          "refunded": {
            "type": "integer",
            "minimum": 0,
            "description": "Video credits refunded to the authenticated workspace during the current UTC month."
          },
          "net_used": {
            "type": "integer",
            "description": "Monthly used credits minus refunded credits for the authenticated workspace."
          },
          "netUsed": {
            "type": "integer",
            "description": "CamelCase alias for net_used."
          },
          "transactions": {
            "type": "integer",
            "minimum": 0,
            "description": "Credit usage/refund transactions counted for the authenticated workspace during the current UTC month."
          },
          "current_api_key": {
            "$ref": "#/components/schemas/UsageApiKeyVideoCredits"
          },
          "currentApiKey": {
            "$ref": "#/components/schemas/UsageApiKeyVideoCredits"
          }
        },
        "additionalProperties": false
      },
      "UsageVideoGenerationBlocker": {
        "type": "object",
        "required": ["code", "reason", "retryable"],
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "insufficient_video_credits",
              "concurrent_job_limit_reached"
            ],
            "description": "Stable machine-readable blocker code for one reason video creation is unavailable."
          },
          "reason": {
            "type": "string",
            "enum": [
              "insufficient_video_credits",
              "concurrent_job_limit_reached"
            ],
            "description": "Alias for code, kept for simple generated clients."
          },
          "retryable": {
            "type": "boolean",
            "description": "Whether retrying later can clear this blocker without buying credits or changing the request."
          },
          "required_credits": {
            "type": "integer",
            "minimum": 1
          },
          "requiredCredits": {
            "type": "integer",
            "minimum": 1,
            "description": "CamelCase alias for required_credits."
          },
          "credits_remaining": {
            "type": "integer",
            "minimum": 0
          },
          "creditsRemaining": {
            "type": "integer",
            "minimum": 0,
            "description": "CamelCase alias for credits_remaining."
          },
          "retry_block_code": {
            "type": "string",
            "maxLength": 120,
            "description": "Stable retry-block code for non-retryable blockers."
          },
          "retryBlockCode": {
            "type": "string",
            "maxLength": 120,
            "description": "CamelCase alias for retry_block_code."
          },
          "concurrent_jobs": {
            "type": "integer",
            "minimum": 1
          },
          "concurrentJobs": {
            "type": "integer",
            "minimum": 1,
            "description": "CamelCase alias for concurrent_jobs."
          },
          "active_jobs": {
            "type": "integer",
            "minimum": 0
          },
          "activeJobs": {
            "type": "integer",
            "minimum": 0,
            "description": "CamelCase alias for active_jobs."
          },
          "available_slots": {
            "type": "integer",
            "minimum": 0
          },
          "availableSlots": {
            "type": "integer",
            "minimum": 0,
            "description": "CamelCase alias for available_slots."
          },
          "active_job_expires_in_seconds": {
            "type": "integer",
            "minimum": 0
          },
          "activeJobExpiresInSeconds": {
            "type": "integer",
            "minimum": 0,
            "description": "CamelCase alias for active_job_expires_in_seconds."
          },
          "retry_after_seconds": {
            "type": "integer",
            "minimum": 0,
            "description": "Best-effort wait before retrying when this is a concurrent-job blocker."
          },
          "retryAfter": {
            "type": "integer",
            "minimum": 0,
            "description": "SDK-style alias for retry_after_seconds."
          },
          "retryAfterSeconds": {
            "type": "integer",
            "minimum": 0,
            "description": "CamelCase alias for retry_after_seconds."
          },
          "next_poll_at": {
            "type": "string",
            "format": "date-time",
            "description": "Absolute UTC retry time when this is a concurrent-job blocker."
          },
          "nextPollAt": {
            "type": "string",
            "format": "date-time",
            "description": "SDK-style alias for next_poll_at."
          }
        },
        "additionalProperties": false
      },
      "UsageVideoGenerationReadiness": {
        "type": "object",
        "required": [
          "can_create",
          "required_credits",
          "credits_remaining",
          "reason"
        ],
        "properties": {
          "can_create": {
            "type": "boolean",
            "description": "True when the authenticated account has enough video credits to submit one Developer API video job."
          },
          "canCreate": {
            "type": "boolean",
            "description": "CamelCase alias for can_create."
          },
          "required_credits": {
            "type": "integer",
            "minimum": 1,
            "description": "Video credits required to submit one Developer API video job."
          },
          "requiredCredits": {
            "type": "integer",
            "minimum": 1,
            "description": "CamelCase alias for required_credits."
          },
          "credits_remaining": {
            "type": "integer",
            "minimum": 0,
            "description": "Current video credit balance used for the readiness check."
          },
          "creditsRemaining": {
            "type": "integer",
            "minimum": 0,
            "description": "CamelCase alias for credits_remaining."
          },
          "concurrent_jobs": {
            "type": "integer",
            "minimum": 1,
            "description": "Current concurrent Developer API video job limit for this account."
          },
          "concurrentJobs": {
            "type": "integer",
            "minimum": 1,
            "description": "CamelCase alias for concurrent_jobs."
          },
          "active_jobs": {
            "type": "integer",
            "minimum": 0,
            "description": "Currently reserved Developer API video jobs for this account."
          },
          "activeJobs": {
            "type": "integer",
            "minimum": 0,
            "description": "CamelCase alias for active_jobs."
          },
          "available_slots": {
            "type": "integer",
            "minimum": 0,
            "description": "Concurrent API video job slots available before create calls will return 429."
          },
          "availableSlots": {
            "type": "integer",
            "minimum": 0,
            "description": "CamelCase alias for available_slots."
          },
          "active_job_expires_in_seconds": {
            "type": "integer",
            "minimum": 0,
            "description": "Best-effort TTL for the active-job counter when there are reserved slots."
          },
          "activeJobExpiresInSeconds": {
            "type": "integer",
            "minimum": 0,
            "description": "CamelCase alias for active_job_expires_in_seconds."
          },
          "retry_after_seconds": {
            "type": "integer",
            "minimum": 0,
            "description": "Best-effort wait before retrying a video create request when reason is concurrent_job_limit_reached."
          },
          "retryAfter": {
            "type": "integer",
            "minimum": 0,
            "description": "SDK-style alias for retry_after_seconds."
          },
          "retryAfterSeconds": {
            "type": "integer",
            "minimum": 0,
            "description": "CamelCase alias for retry_after_seconds used by generated clients."
          },
          "next_poll_at": {
            "type": "string",
            "format": "date-time",
            "description": "Absolute UTC time when clients should retry a video create request when reason is concurrent_job_limit_reached."
          },
          "nextPollAt": {
            "type": "string",
            "format": "date-time",
            "description": "SDK-style alias for next_poll_at."
          },
          "reason": {
            "type": "string",
            "enum": [
              "available",
              "insufficient_video_credits",
              "concurrent_job_limit_reached"
            ],
            "description": "Machine-readable reason for the current video-generation readiness state."
          },
          "blockers": {
            "type": "array",
            "description": "Present when can_create is false. Lists every actionable blocker, so clients can distinguish credit purchase from active-slot backoff.",
            "items": {
              "$ref": "#/components/schemas/UsageVideoGenerationBlocker"
            }
          }
        },
        "additionalProperties": false
      },
      "UsageResponse": {
        "type": "object",
        "required": ["object", "video_credits"],
        "properties": {
          "object": {
            "type": "string",
            "const": "usage"
          },
          "video_credits": {
            "$ref": "#/components/schemas/UsageVideoCredits"
          },
          "videoCredits": {
            "$ref": "#/components/schemas/UsageVideoCredits",
            "description": "CamelCase alias for video_credits."
          },
          "video_generation": {
            "$ref": "#/components/schemas/UsageVideoGenerationReadiness"
          },
          "videoGeneration": {
            "$ref": "#/components/schemas/UsageVideoGenerationReadiness",
            "description": "CamelCase alias for video_generation."
          },
          "creditsTotal": {
            "type": "integer",
            "minimum": 0,
            "description": "Legacy flat total for simple SDK and agent displays."
          },
          "creditsUsed": {
            "type": "integer",
            "description": "Legacy flat monthly net-used total."
          },
          "creditsRemaining": {
            "type": "integer",
            "minimum": 0,
            "description": "Legacy flat current video credit balance."
          },
          "periodStart": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-01$",
            "description": "Legacy flat UTC month start date."
          },
          "periodEnd": {
            "type": "string",
            "format": "date-time",
            "description": "Legacy flat UTC month reset time."
          }
        },
        "additionalProperties": false
      },
      "ApiAudioUploadFallbackDiagnostics": {
        "type": "object",
        "required": ["source", "key", "recoveredFromS3"],
        "properties": {
          "source": {
            "type": "string",
            "const": "api-audio-upload-s3-fallback"
          },
          "key": {
            "type": "string",
            "description": "Sanitized S3 key for the hosted audio object recovered by the worker."
          },
          "recoveredFromS3": {
            "type": "boolean",
            "description": "True when the worker recovered the render by falling back from the signed read URL to the trusted hosted upload object."
          },
          "reason": {
            "type": "string",
            "description": "Redacted worker fallback reason. Signed URL secrets are never included."
          },
          "s3FallbackRetryCount": {
            "type": "integer",
            "minimum": 1,
            "description": "Number of retryable hosted upload S3 fallback failures observed before the terminal fallback result."
          },
          "s3FallbackLastRetryReason": {
            "type": "string",
            "description": "Redacted reason from the most recent retryable hosted upload S3 fallback failure."
          },
          "expectedETag": {
            "type": "string",
            "description": "Queued hosted upload ETag used for worker fallback validation."
          },
          "expectedMimeType": {
            "type": "string",
            "enum": ["audio/wav", "audio/mpeg", "audio/mp4"]
          },
          "expectedSizeBytes": {
            "type": "number",
            "minimum": 0,
            "description": "Queued hosted upload byte length used for worker fallback validation."
          },
          "readUrlExpiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Signed hosted audio read URL expiry known when the worker started."
          },
          "readUrlExpiredAtFallback": {
            "type": "boolean",
            "description": "True when the signed hosted audio read URL had expired at the worker fallback time."
          }
        },
        "additionalProperties": false
      },
      "LivePortraitRecoveryDiagnostics": {
        "type": "object",
        "required": [
          "source",
          "squareDrivingCropBypassAttempted",
          "squareDrivingCropBypassStage"
        ],
        "properties": {
          "source": {
            "type": "string",
            "const": "liveportrait"
          },
          "squareDrivingCropBypassAttempted": {
            "type": "boolean",
            "const": true,
            "description": "True when Puppetry retried the LivePortrait render without square-driving crop after detecting the known failure mode."
          },
          "squareDrivingCropBypassStage": {
            "type": "string",
            "maxLength": 240,
            "description": "Redacted stage marker for the square-driving-crop bypass attempt. Signed URLs and trace secrets are never included."
          }
        },
        "additionalProperties": false
      },
      "VideoJobDiagnostics": {
        "type": "object",
        "properties": {
          "apiAudioUpload": {
            "$ref": "#/components/schemas/ApiAudioUploadFallbackDiagnostics"
          },
          "livePortrait": {
            "$ref": "#/components/schemas/LivePortraitRecoveryDiagnostics"
          }
        },
        "additionalProperties": false
      },
      "VideoJobQueuedResponse": {
        "type": "object",
        "required": [
          "object",
          "id",
          "operation_id",
          "request_id",
          "source",
          "status",
          "video_url",
          "credits",
          "status_url",
          "content_location",
          "created_at",
          "expires_at",
          "retry_after_seconds",
          "next_poll_at",
          "creation_credits"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "video_job"
          },
          "id": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789"
          },
          "job_id": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Alias for id, useful for agent and task-style clients."
          },
          "jobId": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "SDK-style alias for id."
          },
          "task_id": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Legacy task-style alias for id."
          },
          "taskId": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "CamelCase task-style alias for id."
          },
          "operation_id": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Stable public operation id for tracing a Developer API video job. Mirrors id and never exposes the internal animation service job id."
          },
          "operationId": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "SDK-style alias for operation_id."
          },
          "request_id": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Stable public request id for correlating a Developer API video job with backend logs. Mirrors id and never exposes the internal animation service job id."
          },
          "requestId": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "SDK-style alias for request_id."
          },
          "source": {
            "type": "string",
            "enum": ["text", "audio"],
            "description": "Video create source. text means POST /api/v1/videos/text, audio means POST /api/v1/videos/audio."
          },
          "request_source": {
            "type": "string",
            "enum": ["text", "audio"],
            "description": "Alias for source."
          },
          "requestSource": {
            "type": "string",
            "enum": ["text", "audio"],
            "description": "SDK-style alias for source."
          },
          "status": {
            "type": "string",
            "enum": ["queued"]
          },
          "video_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Null until the job is completed."
          },
          "error": {
            "type": ["string", "null"],
            "description": "Null for queued jobs and non-failed Idempotency-Key replays."
          },
          "credits": {
            "$ref": "#/components/schemas/VideoCredits"
          },
          "creation_credits": {
            "$ref": "#/components/schemas/VideoJobCreationCredits",
            "description": "Original create-time video credit debit for this job. Status polling remains no-charge; see credits.charged for the current request."
          },
          "creationCredits": {
            "$ref": "#/components/schemas/VideoJobCreationCredits",
            "description": "SDK-style alias for creation_credits."
          },
          "status_url": {
            "type": "string",
            "example": "/api/v1/videos/api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Relative status URL for the API video job body. Use Location or Content-Location response headers when an absolute poll URL is required."
          },
          "retry_after_seconds": {
            "type": "integer",
            "minimum": 0,
            "example": 5,
            "description": "Seconds to wait before the first status poll. Mirrors the Retry-After response header."
          },
          "idempotent_replay": {
            "type": "boolean",
            "description": "Present and true when an Idempotency-Key replay returns an already accepted job without charging another credit."
          },
          "videoUrl": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "SDK-style alias for video_url."
          },
          "result_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Alias for the completed video URL."
          },
          "resultUrl": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "SDK-style alias for result_url."
          },
          "download_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Alias for the completed video download URL."
          },
          "downloadUrl": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "SDK-style alias for download_url."
          },
          "output_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Alias for agent clients that call the completed video an output URL."
          },
          "outputUrl": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "SDK-style alias for output_url."
          },
          "url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Shortest SDK-style alias for the completed video URL."
          },
          "statusUrl": {
            "type": "string",
            "example": "/api/v1/videos/api_video_8b1d2f4c6a7e9f0123456789",
            "description": "SDK-style alias for status_url."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "SDK-style alias for created_at."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When this API video job record expires from the status/idempotency lookup window."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "SDK-style alias for expires_at."
          },
          "retryAfter": {
            "type": "integer",
            "minimum": 0,
            "example": 5,
            "description": "SDK-style alias for retry_after_seconds."
          },
          "retryAfterSeconds": {
            "type": "integer",
            "minimum": 0,
            "example": 5,
            "description": "CamelCase alias for retry_after_seconds used by generated clients."
          },
          "next_poll_at": {
            "type": "string",
            "format": "date-time",
            "description": "Absolute UTC time when clients should make the next status poll. Present with retry_after_seconds and mirrors the X-Puppetry-Next-Poll-At response header."
          },
          "nextPollAt": {
            "type": "string",
            "format": "date-time",
            "description": "SDK-style alias for next_poll_at."
          },
          "idempotentReplay": {
            "type": "boolean",
            "description": "SDK-style alias for idempotent_replay."
          },
          "content_location": {
            "type": "string",
            "example": "/api/v1/videos/api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Body alias for the status poll URL, mirroring the Content-Location response header target for clients that cannot reliably read headers."
          },
          "contentLocation": {
            "type": "string",
            "example": "/api/v1/videos/api_video_8b1d2f4c6a7e9f0123456789",
            "description": "SDK-style alias for content_location."
          }
        },
        "additionalProperties": false
      },
      "VideoJobReplayResponse": {
        "type": "object",
        "required": [
          "object",
          "id",
          "operation_id",
          "request_id",
          "source",
          "status",
          "video_url",
          "error",
          "credits",
          "status_url",
          "content_location",
          "idempotent_replay"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "video_job"
          },
          "id": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789"
          },
          "job_id": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Alias for id, useful for agent and task-style clients."
          },
          "jobId": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "SDK-style alias for id."
          },
          "task_id": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Legacy task-style alias for id."
          },
          "taskId": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "CamelCase task-style alias for id."
          },
          "operation_id": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Stable public operation id for tracing a Developer API video job. Mirrors id and never exposes the internal animation service job id."
          },
          "operationId": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "SDK-style alias for operation_id."
          },
          "request_id": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Stable public request id for correlating a Developer API video job with backend logs. Mirrors id and never exposes the internal animation service job id."
          },
          "requestId": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "SDK-style alias for request_id."
          },
          "source": {
            "type": "string",
            "enum": ["text", "audio"],
            "description": "Video create source. text means POST /api/v1/videos/text, audio means POST /api/v1/videos/audio."
          },
          "request_source": {
            "type": "string",
            "enum": ["text", "audio"],
            "description": "Alias for source."
          },
          "requestSource": {
            "type": "string",
            "enum": ["text", "audio"],
            "description": "SDK-style alias for source."
          },
          "status": {
            "type": "string",
            "enum": ["completed", "failed"]
          },
          "progress": {
            "type": ["number", "null"],
            "minimum": 0,
            "maximum": 100
          },
          "video_url": {
            "type": ["string", "null"],
            "format": "uri"
          },
          "error": {
            "type": ["string", "null"]
          },
          "retryable": {
            "type": "boolean",
            "description": "Present for failed jobs when the backend knows whether retrying is useful. false means clients should not resubmit the same job."
          },
          "retry_blocked_reason": {
            "type": "string",
            "maxLength": 512,
            "description": "Machine-readable but redacted reason explaining why retryable is false."
          },
          "retryBlockedReason": {
            "type": "string",
            "maxLength": 512,
            "description": "SDK-style alias for retry_blocked_reason."
          },
          "retry_block_code": {
            "type": "string",
            "maxLength": 120,
            "pattern": "^[a-zA-Z0-9._:-]{1,120}$",
            "description": "Stable retry-block code for failed jobs where retryable is false."
          },
          "retryBlockCode": {
            "type": "string",
            "maxLength": 120,
            "pattern": "^[a-zA-Z0-9._:-]{1,120}$",
            "description": "SDK-style alias for retry_block_code."
          },
          "diagnostics": {
            "$ref": "#/components/schemas/VideoJobDiagnostics"
          },
          "credits": {
            "$ref": "#/components/schemas/VideoCredits"
          },
          "creation_credits": {
            "$ref": "#/components/schemas/VideoJobCreationCredits",
            "description": "Original create-time video credit debit for this job when recorded. Idempotency-Key replays remain no-charge; see credits.charged for the current request."
          },
          "creationCredits": {
            "$ref": "#/components/schemas/VideoJobCreationCredits",
            "description": "SDK-style alias for creation_credits."
          },
          "status_url": {
            "type": "string",
            "example": "/api/v1/videos/api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Relative status URL for the API video job body. Use Location or Content-Location response headers when an absolute poll URL is required."
          },
          "idempotent_replay": {
            "type": "boolean",
            "const": true
          },
          "videoUrl": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "SDK-style alias for video_url."
          },
          "result_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Alias for the completed video URL."
          },
          "resultUrl": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "SDK-style alias for result_url."
          },
          "download_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Alias for the completed video download URL."
          },
          "downloadUrl": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "SDK-style alias for download_url."
          },
          "output_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Alias for agent clients that call the completed video an output URL."
          },
          "outputUrl": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "SDK-style alias for output_url."
          },
          "url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Shortest SDK-style alias for the completed video URL."
          },
          "statusUrl": {
            "type": "string",
            "example": "/api/v1/videos/api_video_8b1d2f4c6a7e9f0123456789",
            "description": "SDK-style alias for status_url."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the API video job was created."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "SDK-style alias for created_at."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When this API video job record expires from the status/idempotency lookup window."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "SDK-style alias for expires_at."
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the API video job reached a terminal completed or failed state."
          },
          "completedAt": {
            "type": "string",
            "format": "date-time",
            "description": "SDK-style alias for completed_at."
          },
          "retry_after_seconds": {
            "type": "integer",
            "minimum": 0,
            "example": 5,
            "description": "Present for queued Idempotency-Key replays so clients can pace the next status poll. Mirrors Retry-After."
          },
          "retryAfter": {
            "type": "integer",
            "minimum": 0,
            "example": 5,
            "description": "SDK-style alias for retry_after_seconds."
          },
          "retryAfterSeconds": {
            "type": "integer",
            "minimum": 0,
            "example": 5,
            "description": "CamelCase alias for retry_after_seconds used by generated clients."
          },
          "next_poll_at": {
            "type": "string",
            "format": "date-time",
            "description": "Absolute UTC time when clients should make the next status poll or retry. Present with retry_after_seconds and mirrors X-Puppetry-Next-Poll-At for queued replays."
          },
          "nextPollAt": {
            "type": "string",
            "format": "date-time",
            "description": "SDK-style alias for next_poll_at."
          },
          "idempotentReplay": {
            "type": "boolean",
            "const": true,
            "description": "SDK-style alias for idempotent_replay."
          },
          "content_location": {
            "type": "string",
            "example": "/api/v1/videos/api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Body alias for the status poll URL, mirroring the Content-Location response header target for clients that cannot reliably read headers."
          },
          "contentLocation": {
            "type": "string",
            "example": "/api/v1/videos/api_video_8b1d2f4c6a7e9f0123456789",
            "description": "SDK-style alias for content_location."
          }
        },
        "additionalProperties": false
      },
      "VideoJobStatusResponse": {
        "type": "object",
        "required": [
          "object",
          "id",
          "operation_id",
          "request_id",
          "source",
          "status",
          "progress",
          "video_url",
          "error",
          "credits",
          "created_at",
          "expires_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "video_job"
          },
          "id": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789"
          },
          "job_id": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Alias for id, useful for agent and task-style clients."
          },
          "jobId": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "SDK-style alias for id."
          },
          "task_id": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Legacy task-style alias for id."
          },
          "taskId": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "CamelCase task-style alias for id."
          },
          "operation_id": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Stable public operation id for tracing a Developer API video job. Mirrors id and never exposes the internal animation service job id."
          },
          "operationId": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "SDK-style alias for operation_id."
          },
          "request_id": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Stable public request id for correlating a Developer API video job with backend logs. Mirrors id and never exposes the internal animation service job id."
          },
          "requestId": {
            "type": "string",
            "pattern": "^api_video_",
            "example": "api_video_8b1d2f4c6a7e9f0123456789",
            "description": "SDK-style alias for request_id."
          },
          "source": {
            "type": "string",
            "enum": ["text", "audio"],
            "description": "Video create source. text means POST /api/v1/videos/text, audio means POST /api/v1/videos/audio."
          },
          "request_source": {
            "type": "string",
            "enum": ["text", "audio"],
            "description": "Alias for source."
          },
          "requestSource": {
            "type": "string",
            "enum": ["text", "audio"],
            "description": "SDK-style alias for source."
          },
          "status": {
            "type": "string",
            "enum": ["queued", "processing", "completed", "failed"]
          },
          "progress": {
            "type": ["number", "null"],
            "minimum": 0,
            "maximum": 100
          },
          "video_url": {
            "type": ["string", "null"],
            "format": "uri"
          },
          "error": {
            "type": ["string", "null"]
          },
          "credits": {
            "allOf": [
              {
                "$ref": "#/components/schemas/VideoCredits"
              }
            ],
            "description": "Status polling is no-charge; responses use charged=0 and remaining=null."
          },
          "creation_credits": {
            "$ref": "#/components/schemas/VideoJobCreationCredits",
            "description": "Original create-time video credit debit for this job when recorded. Status polling remains no-charge; see credits.charged for the current request."
          },
          "creationCredits": {
            "$ref": "#/components/schemas/VideoJobCreationCredits",
            "description": "SDK-style alias for creation_credits."
          },
          "retryable": {
            "type": "boolean",
            "description": "Present for failed jobs when the backend knows whether retrying is useful. false means clients should not resubmit the same job."
          },
          "retry_blocked_reason": {
            "type": "string",
            "maxLength": 512,
            "description": "Machine-readable but redacted reason explaining why retryable is false."
          },
          "retryBlockedReason": {
            "type": "string",
            "maxLength": 512,
            "description": "SDK-style alias for retry_blocked_reason."
          },
          "retry_block_code": {
            "type": "string",
            "maxLength": 120,
            "pattern": "^[a-zA-Z0-9._:-]{1,120}$",
            "description": "Stable retry-block code for failed jobs where retryable is false."
          },
          "retryBlockCode": {
            "type": "string",
            "maxLength": 120,
            "pattern": "^[a-zA-Z0-9._:-]{1,120}$",
            "description": "SDK-style alias for retry_block_code."
          },
          "diagnostics": {
            "$ref": "#/components/schemas/VideoJobDiagnostics"
          },
          "retry_after_seconds": {
            "type": "integer",
            "minimum": 0,
            "example": 5,
            "description": "Present for queued, processing, or retryable failed jobs so clients can pace the next status poll or retry. Mirrors the Retry-After response header."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When this API video job record expires from the status lookup window."
          },
          "videoUrl": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "SDK-style alias for video_url."
          },
          "result_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Alias for the completed video URL."
          },
          "resultUrl": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "SDK-style alias for result_url."
          },
          "download_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Alias for the completed video download URL."
          },
          "downloadUrl": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "SDK-style alias for download_url."
          },
          "output_url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Alias for agent clients that call the completed video an output URL."
          },
          "outputUrl": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "SDK-style alias for output_url."
          },
          "url": {
            "type": ["string", "null"],
            "format": "uri",
            "description": "Shortest SDK-style alias for the completed video URL."
          },
          "status_url": {
            "type": "string",
            "example": "/api/v1/videos/api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Relative status URL for the API video job body. Use Location or Content-Location response headers when an absolute poll URL is required."
          },
          "statusUrl": {
            "type": "string",
            "example": "/api/v1/videos/api_video_8b1d2f4c6a7e9f0123456789",
            "description": "SDK-style alias for status_url."
          },
          "retryAfter": {
            "type": "integer",
            "minimum": 0,
            "example": 5,
            "description": "SDK-style alias for retry_after_seconds."
          },
          "retryAfterSeconds": {
            "type": "integer",
            "minimum": 0,
            "example": 5,
            "description": "CamelCase alias for retry_after_seconds used by generated clients."
          },
          "next_poll_at": {
            "type": "string",
            "format": "date-time",
            "description": "Absolute UTC time when clients should make the next status poll or retry. Present with retry_after_seconds."
          },
          "nextPollAt": {
            "type": "string",
            "format": "date-time",
            "description": "SDK-style alias for next_poll_at."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "SDK-style alias for created_at."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "SDK-style alias for expires_at."
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "description": "Present for terminal completed or failed jobs."
          },
          "completedAt": {
            "type": "string",
            "format": "date-time",
            "description": "SDK-style alias for completed_at."
          },
          "content_location": {
            "type": "string",
            "example": "/api/v1/videos/api_video_8b1d2f4c6a7e9f0123456789",
            "description": "Body alias for the status poll URL, mirroring the Content-Location response header target for clients that cannot reliably read headers."
          },
          "contentLocation": {
            "type": "string",
            "example": "/api/v1/videos/api_video_8b1d2f4c6a7e9f0123456789",
            "description": "SDK-style alias for content_location."
          }
        },
        "additionalProperties": false
      },
      "VideoJobCreationCredits": {
        "type": "object",
        "required": ["charged", "remaining_after_debit"],
        "properties": {
          "charged": {
            "type": "integer",
            "minimum": 0,
            "example": 1,
            "description": "Video credits charged when the Developer API video job was created. Historical jobs may omit creation_credits when this evidence was not recorded."
          },
          "remaining_after_debit": {
            "type": "integer",
            "nullable": true,
            "minimum": 0,
            "example": 299,
            "description": "Workspace credit balance immediately after the original create debit, or null when unavailable."
          },
          "remainingAfterDebit": {
            "type": "integer",
            "nullable": true,
            "minimum": 0,
            "example": 299,
            "description": "SDK-style alias for remaining_after_debit."
          }
        },
        "additionalProperties": false
      }
    }
  }
}
