openapi: 3.1.0 info: title: Puppetry Developer API Beta Audio Uploads API 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: [] tags: - name: Audio Uploads paths: /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' components: responses: 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 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 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_...' 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' 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 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 headers: 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' RetryAfter: description: Seconds to wait before retrying a limited/transient request or polling a non-terminal video job again. schema: type: integer example: 30 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 RateLimitLimit: description: Requests allowed in the current one-minute window. schema: type: integer example: 10 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 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 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 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 securitySchemes: developerApiKey: type: http scheme: bearer bearerFormat: Puppetry API key description: 'Use the Authorization header with a live Puppetry API key: Bearer pk_live_...'