ELBO AI, INC Text to Speech API
The Text to Speech API from ELBO AI, INC — 1 operation(s) for text to speech.
The Text to Speech API from ELBO AI, INC — 1 operation(s) for text to speech.
openapi: 3.1.0
info:
title: Puppetry Developer API Beta Audio Uploads Text to Speech 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: Text to Speech
paths:
/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'
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
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
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
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
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
securitySchemes:
developerApiKey:
type: http
scheme: bearer
bearerFormat: Puppetry API key
description: 'Use the Authorization header with a live Puppetry API key: Bearer pk_live_...'