{
  "components": {
    "headers": {
      "Retry-After": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer"
        }
      },
      "X-Audio-Ms": {
        "description": "Duration of the returned audio in milliseconds.",
        "schema": {
          "type": "integer"
        }
      },
      "X-Chars": {
        "description": "Characters metered for this request.",
        "schema": {
          "type": "integer"
        }
      },
      "X-Language": {
        "description": "The language used for synthesis — the requested one, or the auto-detected one when `language` was omitted.",
        "schema": {
          "type": "string"
        }
      },
      "X-RateLimit-Limit": {
        "description": "Requests per minute allowed for this key.",
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Requests left in the current window.",
        "schema": {
          "type": "integer"
        }
      },
      "X-Regeneration": {
        "description": "Present when the request was served as a free regeneration of an identical recent request (same voice, text and settings) and therefore not metered again.",
        "schema": {
          "type": "string"
        }
      },
      "X-Seed": {
        "description": "The requested sampler seed. Present only when the request supplies `seed`; omitted seeds are not returned.",
        "schema": {
          "type": "integer"
        }
      }
    },
    "parameters": {
      "DubbingJobID": {
        "description": "The `job_id` returned by `POST /v1/dubbing`.",
        "in": "path",
        "name": "job_id",
        "required": true,
        "schema": {
          "format": "uuid",
          "type": "string"
        }
      }
    },
    "responses": {
      "BadGateway": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "The voice or alignment service failed."
      },
      "BadRequest": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "Validation failed (missing fields, unsupported language or format, text too long, sample too large, consent sentence missing)."
      },
      "Forbidden": {
        "content": {
          "application/problem+json": {
            "example": {
              "detail": "This API key does not have the \"tts\" scope required for POST /v1/tts. Create a key with that scope in the portal.",
              "status": 403,
              "title": "Insufficient scope",
              "type": "about:blank"
            },
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "The key lacks the scope this operation requires; `detail` names it."
      },
      "GatewayTimeout": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "The alignment service did not answer within 60 seconds."
      },
      "NotFound": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "No such voice, stored generation, transcription or dubbing job for this account — also the answer for a dubbing artefact that is not ready yet."
      },
      "NotImplemented": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "The feature is disabled on this server."
      },
      "PayloadTooLarge": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "The complete request body exceeds this endpoint's byte limit. Reduce the input or upload size before retrying."
      },
      "PaymentRequired": {
        "content": {
          "application/problem+json": {
            "example": {
              "detail": "request exceeds the remaining allowance",
              "status": 402,
              "title": "Allowance exceeded",
              "type": "about:blank"
            },
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "The request exceeds the remaining account or key allowance, including work already reserved."
      },
      "StorageCapacity": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "The account reached its aggregate storage allowance, saved-voice allowance or retained-job limit. Delete unneeded content or change plans before adding more; existing content is preserved. Bytes are released after durable cleanup."
      },
      "StorageUnavailable": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "Storage inventory, disk headroom or global retained-upload capacity is unavailable, or an operation reservation expired. Retry later.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          }
        }
      },
      "SubRip": {
        "content": {
          "text/plain": {
            "example": "1\n00:00:00,120 --\u003e 00:00:02,480\nHello from Era Voice.\n",
            "schema": {
              "type": "string"
            }
          }
        },
        "description": "SubRip subtitles, served inline as UTF-8 text."
      },
      "TooManyRequests": {
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "Request rate or processing capacity exceeded. Retry after the indicated delay. Rate-limit headers describe the per-key request bucket; capacity rejection does not consume speech allowance.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          }
        }
      },
      "Unauthorized": {
        "content": {
          "application/problem+json": {
            "example": {
              "detail": "The API key is unknown or has been revoked.",
              "status": 401,
              "title": "Unauthorized",
              "type": "about:blank"
            },
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        },
        "description": "The key is missing, malformed, unknown or revoked."
      }
    },
    "schemas": {
      "AlignRequest": {
        "properties": {
          "audio": {
            "description": "The audio to align, up to 25 MB.",
            "format": "binary",
            "type": "string"
          },
          "language": {
            "description": "Language name (`english`) or two-letter code (`en`). Default `en`.",
            "type": "string"
          },
          "text": {
            "description": "The transcript of the audio, up to 20,000 characters.",
            "type": "string"
          }
        },
        "required": [
          "audio",
          "text"
        ],
        "type": "object"
      },
      "AlignResult": {
        "properties": {
          "language": {
            "description": "The two-letter code the alignment ran with.",
            "type": "string"
          },
          "words": {
            "items": {
              "$ref": "#/components/schemas/Word"
            },
            "type": "array"
          }
        },
        "required": [
          "words",
          "language"
        ],
        "type": "object"
      },
      "DialogueRequest": {
        "properties": {
          "format": {
            "$ref": "#/components/schemas/Format"
          },
          "language": {
            "$ref": "#/components/schemas/SpeechRequest/properties/language"
          },
          "normalize": {
            "$ref": "#/components/schemas/Normalize"
          },
          "pronunciation": {
            "$ref": "#/components/schemas/SpeechRequest/properties/pronunciation"
          },
          "seed": {
            "$ref": "#/components/schemas/SpeechRequest/properties/seed"
          },
          "speed": {
            "$ref": "#/components/schemas/SpeechRequest/properties/speed"
          },
          "turns": {
            "description": "Ordered turns with at most 10,000 total trimmed Unicode characters.",
            "items": {
              "$ref": "#/components/schemas/DialogueTurn"
            },
            "maxItems": 50,
            "minItems": 1,
            "type": "array"
          }
        },
        "required": [
          "turns"
        ],
        "type": "object"
      },
      "DialogueTurn": {
        "properties": {
          "language": {
            "$ref": "#/components/schemas/SpeechRequest/properties/language"
          },
          "text": {
            "description": "Speech for this turn. Break markup counts as submitted characters; silence alone is invalid.",
            "maxLength": 10000,
            "minLength": 1,
            "type": "string"
          },
          "voice_id": {
            "$ref": "#/components/schemas/SpeechRequest/properties/voice_id"
          }
        },
        "required": [
          "voice_id",
          "text"
        ],
        "type": "object"
      },
      "DubbingAccepted": {
        "description": "The `202` answer to `POST /v1/dubbing`.",
        "properties": {
          "job_id": {
            "format": "uuid",
            "type": "string"
          },
          "status": {
            "enum": [
              "queued"
            ],
            "type": "string"
          }
        },
        "required": [
          "job_id",
          "status"
        ],
        "type": "object"
      },
      "DubbingCreateRequest": {
        "properties": {
          "audio": {
            "description": "The speech to dub — wav/mp3/m4a/mp4/ogg/webm, up to 25 MiB per file and 15 minutes, with a 26 MiB total multipart request allowance. MP4 must contain an audio track; the result is audio and subtitles. Compressed duration is established by transcription; its 128 kbit/s file-size estimate is only for the submit-time quota check.",
            "format": "binary",
            "type": "string"
          },
          "consent_attestation": {
            "description": "Required only when `voice_id` is omitted, because the source speaker is then cloned. Must be exactly the sentence `POST /v1/voices` requires.",
            "enum": [
              "I confirm I have the right to clone this voice and consent to its use."
            ],
            "type": "string"
          },
          "source_language": {
            "default": "auto",
            "description": "The language spoken in the upload. `auto` (the default) detects it during transcription.",
            "enum": [
              "auto",
              "english",
              "chinese",
              "korean",
              "japanese",
              "german",
              "french",
              "russian",
              "portuguese",
              "spanish",
              "italian"
            ],
            "type": "string"
          },
          "target_language": {
            "$ref": "#/components/schemas/Language"
          },
          "voice_id": {
            "description": "A ready voice of yours to speak the dub. Omit it to clone the source speaker from the upload instead — which then requires `consent_attestation`.",
            "format": "uuid",
            "type": "string"
          },
          "webhook_url": {
            "description": "Absolute http(s) URL notified once when the job reaches `done` or `failed` (see `DubbingWebhook`). Private and loopback hosts are refused in production.",
            "format": "uri",
            "type": "string"
          }
        },
        "required": [
          "audio",
          "target_language"
        ],
        "type": "object"
      },
      "DubbingJob": {
        "description": "A dubbing job. The same object appears in the list and in webhook payloads.",
        "properties": {
          "audio_url": {
            "description": "Absolute URL of the dubbed audio; present only when `status` is `done`.",
            "format": "uri",
            "type": "string"
          },
          "chars": {
            "description": "Characters metered for the job.",
            "type": "integer"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "duration_ms": {
            "description": "Duration of the source audio.",
            "type": "integer"
          },
          "error": {
            "description": "Why the job failed; present only when `status` is `failed`.",
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "job_id": {
            "description": "The same value as `id`, under the name the `202` used.",
            "format": "uuid",
            "type": "string"
          },
          "segments": {
            "description": "Always present, empty until transcription produces segments.",
            "items": {
              "$ref": "#/components/schemas/DubbingSegment"
            },
            "type": "array"
          },
          "source_language": {
            "description": "`auto` until transcription detects it, then the detected language.",
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/DubbingStatus"
          },
          "target_language": {
            "$ref": "#/components/schemas/Language"
          },
          "transcript_srt_url": {
            "description": "Absolute URL of the source transcript; present once transcription has finished.",
            "format": "uri",
            "type": "string"
          },
          "translation_srt_url": {
            "description": "Absolute URL of the translated subtitles; present only when `status` is `done`.",
            "format": "uri",
            "type": "string"
          },
          "updated_at": {
            "format": "date-time",
            "type": "string"
          },
          "voice_id": {
            "description": "The voice speaking the dub — the one you passed, or the one cloned from the upload.",
            "format": "uuid",
            "type": "string"
          },
          "webhook_url": {
            "format": "uri",
            "type": "string"
          }
        },
        "required": [
          "id",
          "job_id",
          "status",
          "source_language",
          "target_language",
          "duration_ms",
          "chars",
          "created_at",
          "updated_at",
          "segments"
        ],
        "type": "object"
      },
      "DubbingJobList": {
        "properties": {
          "jobs": {
            "items": {
              "$ref": "#/components/schemas/DubbingJob"
            },
            "type": "array"
          }
        },
        "required": [
          "jobs"
        ],
        "type": "object"
      },
      "DubbingSegment": {
        "description": "One speech segment of the source, with its translation and where the dub placed it.",
        "properties": {
          "dub_end_ms": {
            "type": "integer"
          },
          "dub_start_ms": {
            "description": "Start of the segment in the dubbed audio.",
            "type": "integer"
          },
          "end_ms": {
            "type": "integer"
          },
          "i": {
            "description": "1-based segment index.",
            "type": "integer"
          },
          "start_ms": {
            "description": "Start of the segment in the source audio.",
            "type": "integer"
          },
          "stretch": {
            "description": "Time-stretch factor applied to fit the dub to the source timing (1 = unchanged).",
            "type": "number"
          },
          "text": {
            "description": "The transcribed source speech.",
            "type": "string"
          },
          "translation": {
            "type": "string"
          }
        },
        "required": [
          "i",
          "start_ms",
          "end_ms",
          "text"
        ],
        "type": "object"
      },
      "DubbingStatus": {
        "description": "Where the job has got to. `done` and `failed` are terminal; on\n`failed` the job's `error` says why.\n",
        "enum": [
          "queued",
          "transcribing",
          "translating",
          "synthesizing",
          "assembling",
          "done",
          "failed"
        ],
        "type": "string"
      },
      "DubbingWebhook": {
        "description": "The body POSTed to a job's `webhook_url` once it reaches a terminal\nstate. It is signed with `X-Era-Signature: sha256=\u003chex\u003e`, an\nHMAC-SHA256 of the raw body keyed with the stored hash of the API key\nthat submitted the job. One delivery plus three retries.\n",
        "properties": {
          "event": {
            "enum": [
              "dubbing.done",
              "dubbing.failed"
            ],
            "type": "string"
          },
          "job": {
            "$ref": "#/components/schemas/DubbingJob"
          }
        },
        "required": [
          "event",
          "job"
        ],
        "type": "object"
      },
      "Format": {
        "default": "wav",
        "description": "Output encoding. `wav` is 24 kHz 16-bit PCM in a RIFF container; the\n`mp3_*` values are MP3 at 24 kHz with the given bitrate (kbps); `pcm_*`\nare raw 16-bit little-endian frames at the given sample rate (no\nheader); `ulaw_8000` / `alaw_8000` are raw 8 kHz telephony frames.\n`mp3` is accepted as an alias of `mp3_24000_128`, and `mp3_24000_192`\nas an alias of `mp3_24000_160` (MPEG-2 Layer III defines no 192 kbps\nmode at 24 kHz); the response echoes the resolved name.\n",
        "enum": [
          "wav",
          "mp3_24000_128",
          "mp3_24000_160",
          "pcm_24000",
          "pcm_16000",
          "pcm_8000",
          "ulaw_8000",
          "alaw_8000",
          "mp3",
          "mp3_24000_192"
        ],
        "type": "string"
      },
      "Language": {
        "description": "One of the ten languages the voice model speaks.",
        "enum": [
          "english",
          "chinese",
          "korean",
          "japanese",
          "german",
          "french",
          "russian",
          "portuguese",
          "spanish",
          "italian"
        ],
        "type": "string"
      },
      "Normalize": {
        "default": "auto",
        "description": "Text normalization before synthesis (numbers, dates, units spoken out). `auto` applies it where it helps.",
        "enum": [
          "auto",
          "on",
          "off"
        ],
        "type": "string"
      },
      "Problem": {
        "description": "RFC 7807 problem details.",
        "properties": {
          "detail": {
            "description": "Human-readable explanation; names the missing scope, the cap and the usage where relevant.",
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "title": {
            "description": "Short, stable summary — e.g. `Unauthorized`, `Insufficient scope`, `Plan limit reached`, `Key cap reached`.",
            "type": "string"
          },
          "type": {
            "description": "Always `about:blank`.",
            "type": "string"
          }
        },
        "required": [
          "type",
          "title",
          "status"
        ],
        "type": "object"
      },
      "SpeechRequest": {
        "properties": {
          "format": {
            "$ref": "#/components/schemas/Format"
          },
          "language": {
            "description": "Language of the text. Optional — omitted means auto-detect (the choice is echoed in `X-Language`).",
            "enum": [
              "auto",
              "english",
              "chinese",
              "korean",
              "japanese",
              "german",
              "french",
              "russian",
              "portuguese",
              "spanish",
              "italian"
            ],
            "type": "string"
          },
          "normalize": {
            "$ref": "#/components/schemas/Normalize"
          },
          "pronunciation": {
            "default": "auto",
            "description": "Apply the account pronunciation dictionary (auto), or bypass it (off). Rules are exact, case-sensitive word/phrase respellings, applied once with longest-match and language-specific precedence. Replacements are protected from number normalization. Clients cannot supply trusted replay snapshots.",
            "enum": [
              "auto",
              "off"
            ],
            "type": "string"
          },
          "seed": {
            "description": "Optional sampler seed for reproducible output. A supplied value is echoed in `X-Seed`; the header is absent when this field is omitted.",
            "maximum": 4294967295,
            "minimum": 0,
            "type": "integer"
          },
          "speed": {
            "default": 1,
            "description": "Pitch-preserving speech speed multiplier. Explicit pauses retain their requested duration at any speed. Omitted means 1; zero is invalid. Non-default speed returns an upstream service error if time stretching is unavailable.",
            "maximum": 2,
            "minimum": 0.5,
            "type": "number"
          },
          "text": {
            "description": "Text to speak. Up to 10,000 characters for `/v1/tts`, 20,000 for `/v1/tts/stream`. Explicit pauses use only `\u003cbreak time=\"500ms\"/\u003e` or `\u003cbreak time=\"1s\"/\u003e` syntax: 0–10 seconds per break, at most 128 breaks and 60 seconds of total silence. At least one spoken character is required. Malformed breaks are rejected; this is not general SSML support. Character usage counts submitted text, including break markup, before pronunciation expansion.",
            "minLength": 1,
            "type": "string"
          },
          "voice_id": {
            "description": "A voice from `GET /v1/voices` (your own or a library preset).",
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "text",
          "voice_id"
        ],
        "type": "object"
      },
      "StreamChunkEvent": {
        "properties": {
          "audio_base64": {
            "description": "One sentence chunk of audio, base64-encoded, complete on its own (a full WAV or MP3 frame sequence).",
            "type": "string"
          },
          "duration_ms": {
            "type": "integer"
          },
          "format": {
            "type": "string"
          },
          "seq": {
            "description": "1-based chunk index.",
            "type": "integer"
          }
        },
        "required": [
          "seq",
          "audio_base64",
          "duration_ms",
          "format"
        ],
        "type": "object"
      },
      "StreamDoneEvent": {
        "properties": {
          "chars": {
            "description": "Characters metered for the whole stream.",
            "type": "integer"
          },
          "done": {
            "enum": [
              true
            ],
            "type": "boolean"
          },
          "total_duration_ms": {
            "type": "integer"
          }
        },
        "required": [
          "done",
          "total_duration_ms",
          "chars"
        ],
        "type": "object"
      },
      "StreamErrorEvent": {
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ],
        "type": "object"
      },
      "TimestampedSpeech": {
        "description": "The `?timestamps=word` response.",
        "properties": {
          "audio_base64": {
            "description": "The audio bytes, base64-encoded.",
            "type": "string"
          },
          "duration_ms": {
            "type": "integer"
          },
          "format": {
            "description": "The audio format that was produced.",
            "type": "string"
          },
          "words": {
            "items": {
              "$ref": "#/components/schemas/Word"
            },
            "type": "array"
          }
        },
        "required": [
          "audio_base64",
          "format",
          "duration_ms",
          "words"
        ],
        "type": "object"
      },
      "TranscriptionJob": {
        "properties": {
          "completed_at": {
            "format": "date-time",
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "duration_ms": {
            "description": "Measured decoded input duration, rounded up to the next millisecond.",
            "format": "int64",
            "maximum": 900000,
            "minimum": 0,
            "type": "integer"
          },
          "error_code": {
            "description": "Stable failure code, such as invalid_audio, audio_too_long, allowance_exceeded, decoder_unavailable, transcription_timeout or invalid_transcript.",
            "type": "string"
          },
          "error_message": {
            "type": "string"
          },
          "expires_at": {
            "format": "date-time",
            "type": "string"
          },
          "filename": {
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "language": {
            "description": "Detected short code, or empty until available.",
            "type": "string"
          },
          "status": {
            "enum": [
              "queued",
              "processing",
              "done",
              "failed",
              "expired"
            ],
            "type": "string"
          },
          "transcript": {
            "description": "Available only for completed, unexpired individual job reads; omitted from list responses.",
            "properties": {
              "segments": {
                "items": {
                  "properties": {
                    "end_ms": {
                      "minimum": 0,
                      "type": "integer"
                    },
                    "start_ms": {
                      "minimum": 0,
                      "type": "integer"
                    },
                    "text": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "text",
                    "start_ms",
                    "end_ms"
                  ],
                  "type": "object"
                },
                "type": "array"
              },
              "text": {
                "type": "string"
              },
              "words": {
                "items": {
                  "$ref": "#/components/schemas/Word"
                },
                "type": "array"
              }
            },
            "required": [
              "text",
              "segments",
              "words"
            ],
            "type": "object"
          },
          "updated_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "status",
          "filename",
          "language",
          "duration_ms",
          "created_at",
          "updated_at"
        ],
        "type": "object"
      },
      "TranscriptionRequest": {
        "additionalProperties": false,
        "properties": {
          "audio": {
            "description": "WAV/MP3/M4A/MP4/WebM/OGG, up to 25 MiB and 15 decoded minutes.",
            "format": "binary",
            "type": "string"
          },
          "language": {
            "default": "auto",
            "description": "auto or a supported language name/short code; automatic detection can return other detected short codes.",
            "type": "string"
          }
        },
        "required": [
          "audio"
        ],
        "type": "object"
      },
      "Usage": {
        "properties": {
          "audio_seconds": {
            "type": "integer"
          },
          "character_allowance": {
            "properties": {
              "limit": {
                "enum": [
                  2500,
                  250000,
                  1000000
                ],
                "format": "int64",
                "type": "integer"
              },
              "period": {
                "enum": [
                  "week",
                  "month"
                ],
                "type": "string"
              },
              "resets_at": {
                "description": "Exclusive allowance end and next reset in UTC.",
                "format": "date-time",
                "type": "string"
              },
              "starts_at": {
                "description": "Inclusive allowance start in UTC.",
                "format": "date-time",
                "type": "string"
              },
              "used": {
                "description": "Settled characters charged to the current admission period; excludes pending reservations.",
                "format": "int64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "period",
              "limit",
              "used",
              "starts_at",
              "resets_at"
            ],
            "type": "object"
          },
          "chars": {
            "description": "Characters metered so far this month.",
            "type": "integer"
          },
          "generations": {
            "type": "integer"
          },
          "limits": {
            "properties": {
              "dubbing_active_jobs": {
                "enum": [
                  1,
                  2,
                  3
                ],
                "type": "integer"
              },
              "speech_concurrency": {
                "enum": [
                  1,
                  2,
                  3
                ],
                "type": "integer"
              },
              "studio_projects": {
                "enum": [
                  5,
                  25,
                  100
                ],
                "type": "integer"
              },
              "voices": {
                "enum": [
                  3,
                  10,
                  30
                ],
                "type": "integer"
              }
            },
            "required": [
              "voices",
              "studio_projects",
              "speech_concurrency",
              "dubbing_active_jobs"
            ],
            "type": "object"
          },
          "month": {
            "description": "The calendar month, `YYYY-MM` (UTC).",
            "type": "string"
          },
          "plan": {
            "enum": [
              "free",
              "starter",
              "pro"
            ],
            "type": "string"
          },
          "storage": {
            "properties": {
              "limit_bytes": {
                "enum": [
                  250000000,
                  5000000000,
                  25000000000
                ],
                "format": "int64",
                "type": "integer"
              },
              "reserved_bytes": {
                "description": "Bytes held for receiving, running or interrupted work awaiting reconciliation.",
                "format": "int64",
                "minimum": 0,
                "type": "integer"
              },
              "used_bytes": {
                "description": "Retained published blob and audio-preview bytes.",
                "format": "int64",
                "minimum": 0,
                "type": "integer"
              },
              "warning": {
                "description": "True when used plus reserved bytes reach at least 80% of the allowance.",
                "type": "boolean"
              }
            },
            "required": [
              "used_bytes",
              "reserved_bytes",
              "limit_bytes",
              "warning"
            ],
            "type": "object"
          }
        },
        "required": [
          "month",
          "plan",
          "character_allowance",
          "storage",
          "limits",
          "chars",
          "audio_seconds",
          "generations"
        ],
        "type": "object"
      },
      "Voice": {
        "properties": {
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "description": {
            "description": "The prompt a designed voice was made from.",
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "language_hint": {
            "description": "The language the sample was recorded in, when given.",
            "type": "string"
          },
          "library": {
            "description": "True for the shared preset library.",
            "type": "boolean"
          },
          "name": {
            "type": "string"
          },
          "origin": {
            "enum": [
              "cloned",
              "preset",
              "designed",
              "marketplace"
            ],
            "type": "string"
          },
          "status": {
            "enum": [
              "pending",
              "ready",
              "failed"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "status",
          "origin",
          "library",
          "created_at"
        ],
        "type": "object"
      },
      "VoiceCreateRequest": {
        "properties": {
          "consent_attestation": {
            "description": "Must be exactly \"I confirm I have the right to clone this voice and consent to its use.\"",
            "enum": [
              "I confirm I have the right to clone this voice and consent to its use."
            ],
            "type": "string"
          },
          "language_hint": {
            "$ref": "#/components/schemas/Language"
          },
          "name": {
            "type": "string"
          },
          "sample": {
            "description": "Speech sample — wav, mp3, m4a, mp4, ogg or webm — up to 10 MB. The extension decides the format.",
            "format": "binary",
            "type": "string"
          }
        },
        "required": [
          "sample",
          "name",
          "consent_attestation"
        ],
        "type": "object"
      },
      "VoiceDesignRequest": {
        "properties": {
          "description": {
            "description": "What the voice should sound like, e.g. \"a calm elderly storyteller\".",
            "type": "string"
          },
          "language": {
            "$ref": "#/components/schemas/Language"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "name",
          "description",
          "language"
        ],
        "type": "object"
      },
      "VoiceList": {
        "properties": {
          "voices": {
            "items": {
              "$ref": "#/components/schemas/Voice"
            },
            "type": "array"
          }
        },
        "required": [
          "voices"
        ],
        "type": "object"
      },
      "Word": {
        "properties": {
          "end_ms": {
            "description": "End of the word in milliseconds.",
            "type": "integer"
          },
          "start_ms": {
            "description": "Start of the word in milliseconds from the beginning of the audio.",
            "type": "integer"
          },
          "word": {
            "type": "string"
          }
        },
        "required": [
          "word",
          "start_ms",
          "end_ms"
        ],
        "type": "object"
      }
    },
    "securitySchemes": {
      "bearerKey": {
        "bearerFormat": "Era Voice API key (ev_…)",
        "description": "An API key created on `/app/keys`. Its scopes decide which operations it may call.",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "externalDocs": {
    "description": "Human-readable reference with curl examples and SDK quickstarts",
    "url": "https://voice.erasmuslabs.ai/docs"
  },
  "info": {
    "contact": {
      "email": "era-support@erasmuslabs.ai",
      "name": "Era Voice support"
    },
    "description": "Voice cloning and text-to-speech. Clone a voice from a short sample, then\ngenerate speech in ten languages.\n\n**Authentication.** Every `/v1` request carries `Authorization: Bearer ev_…`.\nKeys are minted in the portal (`/app/keys`) with a set of **scopes** and an\noptional **monthly character cap** and a separate **transcription-seconds cap**. Each operation below names the scope it\nneeds in `x-required-scope`; a key without it is refused with `403`.\n\n| Scope | Unlocks |\n|---|---|\n| `tts` | `POST /v1/tts`, `POST /v1/tts/stream`, `POST /v1/dialogue`, MCP `voice_speak`, `voice_dialogue` |\n| `voices:read` | `GET /v1/voices`, MCP `voice_list` |\n| `voices:write` | `POST /v1/voices`, `POST /v1/voices/design`, MCP `voice_clone` |\n| `audio:read` | `GET /v1/audio/{generation_id}` |\n| `usage:read` | `GET /v1/usage` |\n| `align` | `POST /v1/align` |\n| `dubbing` | the `/v1/dubbing` endpoints |\n| `transcription` | the `/v1/transcriptions` endpoints |\n\n**Metering.** Speech generation is metered by character count against the\naccount's plan cap (`402 Plan limit reached`) and, when the key has one, the\nkey's own monthly cap. Shared reservations protect concurrent requests across\nspeech, streaming, dialogue, Studio and dubbing. Transcription uses a separate\nmeasured input-audio allowance: default Free 30, Starter 300 and Pro 1,200\nminutes per month, configurable by deployment. Alignment is free. Each\nreservation belongs to its UTC admission month; completion time is recorded\nseparately. Failed transcription jobs release their reservation without charge.\n\n**Rate limits.** Per key on `/v1`: `429` with `Retry-After`,\n`X-RateLimit-Limit` and `X-RateLimit-Remaining`.\n\n**Plan capacity.** Saved content and active processing share the account's\nplan, across portal and API keys:\n\n| Plan | Retained storage | Saved voices | Studio projects | Concurrent speech | Active dubbing jobs |\n|---|---:|---:|---:|---:|---:|\n| Free | 250 MB | 3 | 5 | 1 | 1 |\n| Starter | 5 GB | 10 | 25 | 2 | 2 |\n| Pro | 25 GB | 30 | 100 | 3 | 3 |\n\nStorage uses decimal bytes (1 MB = 1,000,000 bytes; 1 GB = 1,000,000,000\nbytes). It includes retained samples, generated audio, voice design previews,\nimports, transcription sources, dubbing audio, reviews and exports. Pending\nwork reserves storage before writing. Deleting content frees its charge only\nafter durable cleanup; interrupted or uncertain work can remain reserved until\nreconciliation. A downgrade preserves existing content but blocks growth above\nthe new allowance. `GET /v1/usage` reports current storage and plan limits.\n\n**Capacity and request size.** Speech admission permits the plan's in-flight\noperations per account and four across the application instance, shared by\nordinary speech, streaming, dialogue and Studio. A busy request receives\n`429` with `Retry-After` before reserving speech allowance. Dubbing synthesis\nwaits within its job deadline. These concurrency limits and rate buckets are\nlocal to the current single application instance.\nComplete request bodies are bounded before parsing, including chunked bodies;\noversized bodies receive `413`. Upload files also have their own size limits.\n\nVoice creation permits one expensive operation per account and two per\ninstance. Dubbing admits twelve active jobs globally; retained original\nuploads are limited to 2 GiB globally, with at most 100 saved jobs per account\nand 1,000 globally. Existing content is preserved when a ceiling is reached.\nAccount storage or saved-count ceilings return `409`; temporary storage\npressure or unavailable inventory returns `503` with `Retry-After` where\nprovided. Disk headroom is checked before upload spooling and blob writes.\n\n**Errors** are RFC 7807 `application/problem+json` (`Problem` below).\n\nThe MCP server at `POST /mcp` (same keys) is documented at `/docs/mcp`.\n",
    "termsOfService": "https://voice.erasmuslabs.ai/legal/terms",
    "title": "Era Voice API",
    "version": "1.0"
  },
  "openapi": "3.1.0",
  "paths": {
    "/audio-native/{token}": {
      "get": {
        "description": "The HMAC-signed URL is the bearer credential. Tampered, expired, revoked, or unknown links return `404`; anyone holding a live link can play it.",
        "operationId": "playAudioNativeEmbed",
        "parameters": [
          {
            "in": "path",
            "name": "token",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "audio/*": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "Cached generated audio."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [],
        "summary": "Play signed Audio Native speech",
        "tags": [
          "audio"
        ]
      }
    },
    "/v1/align": {
      "post": {
        "description": "Send audio (up to 25 MB) together with its transcript (up to 20,000\ncharacters) and receive millisecond word timings from the alignment\nservice. `language` is an Era Voice language name (`english`) or a\ntwo-letter code (`en`); it defaults to `en`. The call has a 60-second\nbudget (`504` when exceeded). Not metered — it costs no characters —\nbut rate-limited per key.\n",
        "operationId": "alignAudio",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "encoding": {
                "audio": {
                  "contentType": "audio/wav, audio/mpeg, audio/mp4, audio/ogg, audio/flac, audio/webm"
                }
              },
              "schema": {
                "$ref": "#/components/schemas/AlignRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AlignResult"
                }
              }
            },
            "description": "Word timings."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "504": {
            "$ref": "#/components/responses/GatewayTimeout"
          }
        },
        "summary": "Forced alignment — word timestamps for your own audio",
        "tags": [
          "align"
        ],
        "x-required-scope": "align"
      }
    },
    "/v1/audio-native": {
      "post": {
        "description": "The URL is a cache identity and is never fetched. It must be absolute\npublic HTTPS without credentials or a fragment. Account, canonical URL,\nexact text hash, voice, language, and format identify a cache entry.\nAn eligible entry is reused for 24 hours without new synthesis or usage;\nchanged content/options or expiry invokes ordinary bounded TTS. The\nreturned signed playback URL is a bearer link valid until `expires_at`.\n",
        "operationId": "createAudioNativeEmbed",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "format": {
                    "$ref": "#/components/schemas/Format"
                  },
                  "language": {
                    "$ref": "#/components/schemas/Language"
                  },
                  "text": {
                    "maxLength": 10000,
                    "minLength": 1,
                    "type": "string"
                  },
                  "url": {
                    "format": "uri",
                    "maxLength": 2048,
                    "type": "string"
                  },
                  "voice_id": {
                    "format": "uuid",
                    "type": "string"
                  }
                },
                "required": [
                  "url",
                  "text",
                  "voice_id"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "audio_url": {
                      "format": "uri",
                      "type": "string"
                    },
                    "cached": {
                      "type": "boolean"
                    },
                    "canonical_url": {
                      "format": "uri",
                      "type": "string"
                    },
                    "expires_at": {
                      "format": "date-time",
                      "type": "string"
                    },
                    "id": {
                      "format": "uuid",
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "canonical_url",
                    "audio_url",
                    "expires_at",
                    "cached"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "New or cached Audio Native link."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        },
        "summary": "Create or reuse URL-addressed cached speech",
        "tags": [
          "tts",
          "audio"
        ],
        "x-required-scope": "tts"
      }
    },
    "/v1/audio/{generation_id}": {
      "get": {
        "description": "Audio persisted by the MCP `voice_speak` tool, downloadable with the same key (account-scoped).",
        "operationId": "getGenerationAudio",
        "parameters": [
          {
            "in": "path",
            "name": "generation_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "audio/mpeg": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              },
              "audio/wav": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "The stored audio."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "summary": "Download stored generation audio",
        "tags": [
          "audio"
        ],
        "x-required-scope": "audio:read"
      }
    },
    "/v1/dialogue": {
      "post": {
        "description": "Stitch 1–50 ordered speaker turns with no implicit gap. Each turn must contain\nspeech and name a ready owned or library voice. At most 10,000 total trimmed\nUnicode input characters and 256 KiB JSON. Turn language overrides the global\nlanguage; auto detection is per turn. All other controls are shared with TTS.\n\nThe entire request uses one pronunciation dictionary snapshot. Combined limits\nare 20,000 replacement characters, 100,000 normalized characters, 128 break tags,\n60 seconds of explicit silence, 128 synthesis calls, 15 minutes of decoded audio,\nand 64 MiB encoded output. The work deadline is 115 seconds, with up to five\nadditional seconds to resolve an uncertain accounting outcome after a lost\ncommit acknowledgement.\n\nAll turns are validated and rendered before one accounting transaction. A failure\nbefore commit charges nothing and returns no partial audio. Two identical reruns\nwithin two hours of the latest paid whole dialogue are free, including at the cap;\nseed changes do not affect eligibility. Ordered voices, resolved languages,\neffective speech/pauses, speed and output format determine identity. Repeated\nlines within the first request remain billable. A paid dialogue counts once in\nusage. Plan and key caps are checked before synthesis and again at settlement\nagainst committed usage; this does not reserve quota across other API writers.\n\nAudio is returned directly. X-Dialogue-ID identifies accounting metadata, not a\nstored download. X-Language appears only when all turns resolve to one language.\nTimestamps and streaming modes are unsupported, including body or query fields.\nA connection failure after commit does not automatically refund completed audio.\nAn error with an unresolved accounting outcome may still have committed and\nincurred a charge.\n",
        "operationId": "createDialogue",
        "requestBody": {
          "content": {
            "application/json": {
              "example": {
                "format": "wav",
                "language": "auto",
                "seed": 42,
                "turns": [
                  {
                    "text": "Welcome to Era.",
                    "voice_id": "2b7f0c7e-4d2a-4b6e-9d0e-6f1c1a2b3c4d"
                  },
                  {
                    "text": "\u003cbreak time=\"500ms\"/\u003eThank you.",
                    "voice_id": "7d7f0c7e-4d2a-4b6e-9d0e-6f1c1a2b3c4d"
                  }
                ]
              },
              "schema": {
                "$ref": "#/components/schemas/DialogueRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "audio/*": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "Completed audio in the requested format.",
            "headers": {
              "X-Audio-Ms": {
                "$ref": "#/components/headers/X-Audio-Ms"
              },
              "X-Chars": {
                "$ref": "#/components/headers/X-Chars"
              },
              "X-Dialogue-ID": {
                "description": "Accounting request ID; no stored audio download is associated with this ID.",
                "schema": {
                  "format": "uuid",
                  "type": "string"
                }
              },
              "X-Language": {
                "description": "Present only when every turn resolves to the same language.",
                "schema": {
                  "type": "string"
                }
              },
              "X-Regeneration": {
                "$ref": "#/components/headers/X-Regeneration"
              },
              "X-Seed": {
                "$ref": "#/components/headers/X-Seed"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "JSON body exceeds 256 KiB."
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "504": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Dialogue exceeded its 115-second work deadline. Resolving an uncertain accounting outcome may take up to five additional seconds; an unresolved outcome may still have committed."
          }
        },
        "summary": "Generate ordered multi-voice dialogue",
        "tags": [
          "tts"
        ],
        "x-required-scope": "tts"
      }
    },
    "/v1/dubbing": {
      "get": {
        "description": "The account's dubbing jobs, newest first, capped at 200. No pagination.",
        "operationId": "listDubbingJobs",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DubbingJobList"
                }
              }
            },
            "description": "The account's dubbing jobs."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "summary": "List dubbing jobs",
        "tags": [
          "dubbing"
        ],
        "x-required-scope": "dubbing"
      },
      "post": {
        "description": "Upload speech and get it back spoken in another language, in the same\nvoice. The job is asynchronous: the call returns `202` immediately with\na `job_id`, then walks through `queued` → `transcribing` → `translating`\n→ `synthesizing` → `assembling` → `done` (or `failed`). Poll\n`GET /v1/dubbing/{job_id}` or supply `webhook_url`.\n\nWith `voice_id` the dub is spoken by an existing ready voice of yours.\nWithout it the source speaker is cloned from the upload, and\n`consent_attestation` becomes required — the same sentence\n`POST /v1/voices` demands.\n\nThe audio file is limited to 25 MiB and the complete multipart request\nto 26 MiB, including fields and framing. A request above the total\nallowance returns `413`; an audio file above 25 MiB within that\nallowance returns `400`.\n\nThe recording must be at most 15 minutes. PCM16 WAV duration is checked\nat upload time. Compressed input uses a 128 kbit/s file-size estimate\nonly for the submit-time quota check, not for rejecting its duration.\nThe worker checks the transcription service's reported duration and\nfinal segment timestamp before translation or synthesis; an overlong\nrecording becomes a failed job.\n\nSubmit-time metering uses an estimate of recording length. After translation,\nthe exact translated character count is reserved against both account and\nkey allowances before synthesis. Output, aggregate generation metadata and\none usage event commit atomically when assembly finishes. Failed attempts\nrelease their reservation; stale attempts cannot publish or charge. Dubbing\nhas no free reruns and does not consume standalone transcription minutes.\nUploads reserve their exact bytes. A queued job also needs space reserved\nfor its generated output before processing; it waits when the account\ncannot fit that reservation. Interrupted storage reservations stay charged\nuntil their writers have stopped and cleanup is confirmed.\nA submit that would not fit the remaining allowance is refused with\n`402`, whose `detail` names the plan's limit, the characters used and\nthe estimate for this dub.\n",
        "operationId": "createDubbingJob",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "encoding": {
                "audio": {
                  "contentType": "audio/wav, audio/mpeg, audio/mp4, video/mp4, audio/ogg, audio/webm"
                }
              },
              "schema": {
                "$ref": "#/components/schemas/DubbingCreateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DubbingAccepted"
                }
              }
            },
            "description": "The job was accepted and is queued."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/StorageCapacity"
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "The complete multipart request exceeds the 26 MiB allowance (25 MiB audio file plus 1 MiB for fields and framing)."
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/StorageUnavailable"
          }
        },
        "summary": "Dub audio into another language",
        "tags": [
          "dubbing"
        ],
        "x-required-scope": "dubbing"
      }
    },
    "/v1/dubbing/{job_id}": {
      "delete": {
        "description": "Removes the job and its files. A voice cloned for the job is kept — it belongs to the account.",
        "operationId": "deleteDubbingJob",
        "responses": {
          "204": {
            "description": "Deleted; no body."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "summary": "Delete a dubbing job",
        "tags": [
          "dubbing"
        ],
        "x-required-scope": "dubbing"
      },
      "get": {
        "description": "The job's current status, segments and — once `done` — the URLs of the dubbed audio and the subtitle tracks.",
        "operationId": "getDubbingJob",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DubbingJob"
                }
              }
            },
            "description": "The job."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "summary": "Read one dubbing job",
        "tags": [
          "dubbing"
        ],
        "x-required-scope": "dubbing"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/DubbingJobID"
        }
      ]
    },
    "/v1/dubbing/{job_id}/audio": {
      "get": {
        "description": "The finished dub as a WAV attachment. Answers `404` while the job is not `done`, exactly as for an unknown job.",
        "operationId": "getDubbingAudio",
        "responses": {
          "200": {
            "content": {
              "audio/wav": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "The dubbed audio."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "summary": "Download the dubbed audio",
        "tags": [
          "dubbing"
        ],
        "x-required-scope": "dubbing"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/DubbingJobID"
        }
      ]
    },
    "/v1/dubbing/{job_id}/transcript.srt": {
      "get": {
        "description": "The transcribed source speech as SubRip subtitles. `404` until transcription has finished.",
        "operationId": "getDubbingTranscript",
        "responses": {
          "200": {
            "$ref": "#/components/responses/SubRip"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "summary": "Download the source transcript as SubRip",
        "tags": [
          "dubbing"
        ],
        "x-required-scope": "dubbing"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/DubbingJobID"
        }
      ]
    },
    "/v1/dubbing/{job_id}/translation.srt": {
      "get": {
        "description": "The translated speech as SubRip subtitles. `404` until the job is `done`.",
        "operationId": "getDubbingTranslation",
        "responses": {
          "200": {
            "$ref": "#/components/responses/SubRip"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "summary": "Download the translation as SubRip",
        "tags": [
          "dubbing"
        ],
        "x-required-scope": "dubbing"
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/DubbingJobID"
        }
      ]
    },
    "/v1/transcriptions": {
      "get": {
        "description": "Newest-first account-owned jobs. Transcript content is omitted; retrieve individual jobs for results.",
        "operationId": "listTranscriptions",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 20,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "The next_cursor from a previous response.",
            "in": "query",
            "name": "before",
            "schema": {
              "maxLength": 256,
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "jobs": {
                      "items": {
                        "$ref": "#/components/schemas/TranscriptionJob"
                      },
                      "type": "array"
                    },
                    "next_cursor": {
                      "description": "Empty when no further page is available.",
                      "type": "string"
                    }
                  },
                  "required": [
                    "jobs",
                    "next_cursor"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "A page of job metadata."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "summary": "List transcription job metadata",
        "tags": [
          "transcription"
        ],
        "x-required-scope": "transcription"
      },
      "post": {
        "description": "Upload one WAV, MP3, M4A, MP4, WebM or OGG file (25 MiB maximum;\n26 MiB for the entire multipart body). The first audio stream must decode\nto at most 15 minutes. Language defaults to auto; supported names or\ntwo-letter codes may be supplied. Only one audio and one language field\nare accepted. Audio is decoded and its duration measured before inference;\na queued job may later fail because it exceeds duration or remaining allowance.\n\nInput audio consumes a separate monthly allowance, measured in milliseconds,\nwith optional per-key transcription-second caps. A successful silent recording\nstill consumes its measured duration. Source audio is removed on terminal\nsuccess/failure; transcript content expires after 30 days by default. Both\navailability and retention are configurable. Expired jobs retain accounting\nmetadata but no transcript. Deleting a job removes its content without reversing usage.\n\nThere may be up to ten active or cleanup-pending recordings per account.\nSDK batch helpers submit independent jobs, at most four concurrently, and\npreserve each file's outcome. A lost POST response is not automatically retried;\ninspect recent jobs before submitting again to avoid duplicates.\n",
        "operationId": "createTranscription",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TranscriptionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "id": {
                      "format": "uuid",
                      "type": "string"
                    },
                    "status": {
                      "enum": [
                        "queued"
                      ],
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "status"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "The recording was queued. Poll the returned job.",
            "headers": {
              "Location": {
                "description": "Relative URL of the transcription job.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "The account storage allowance is full, or the receiving upload expired before it was queued."
          },
          "413": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "The audio file or complete multipart body is too large."
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Transcription is disabled, its bounded decoder or storage inventory is unavailable, storage headroom is exhausted, or submission commit outcome is unknown. Inspect recent jobs before resubmitting an uncertain upload."
          }
        },
        "summary": "Queue one recording for transcription",
        "tags": [
          "transcription"
        ],
        "x-required-scope": "transcription"
      }
    },
    "/v1/transcriptions/{id}": {
      "delete": {
        "description": "Fences active processing and clears source/result content. Previously completed usage remains. Pending physical cleanup retries; normal backup retention applies separately.",
        "operationId": "deleteTranscription",
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "summary": "Delete a transcription and its content",
        "tags": [
          "transcription"
        ],
        "x-required-scope": "transcription"
      },
      "get": {
        "description": "Completed jobs include text, segments and words with millisecond timing. Silence succeeds with empty text and arrays. Expired content is never served, even before its storage cleanup runs.",
        "operationId": "getTranscription",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TranscriptionJob"
                }
              }
            },
            "description": "Current status and optional completed transcript."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "summary": "Poll a job and retrieve its transcript",
        "tags": [
          "transcription"
        ],
        "x-required-scope": "transcription"
      },
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "format": "uuid",
            "type": "string"
          }
        }
      ]
    },
    "/v1/tts": {
      "post": {
        "description": "Synthesize `text` with `voice_id`. Up to 10,000 characters per request\n(use `/v1/tts/stream` for longer text). Returns the audio bytes in the\nrequested `format`; with `?timestamps=word` the response is JSON carrying\nthe audio as base64 plus word-level timings from the alignment service\n(`words` is empty when alignment is unavailable — the audio always comes\nback). Every request is metered by character count. `style` and `tags`\nare rejected here because clone synthesis cannot apply them; use the\nseparately gated `/v1/tts/expressive` route with a built-in speaker.\n",
        "operationId": "createSpeech",
        "parameters": [
          {
            "description": "Set to `word` to receive JSON with word-level timings instead of raw audio.",
            "in": "query",
            "name": "timestamps",
            "required": false,
            "schema": {
              "enum": [
                "word"
              ],
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "examples": {
                "mp3": {
                  "summary": "English, MP3 at 128 kbps",
                  "value": {
                    "format": "mp3_24000_128",
                    "language": "english",
                    "text": "Hello from Era Voice.",
                    "voice_id": "2b7f0c7e-4d2a-4b6e-9d0e-6f1c1a2b3c4d"
                  }
                },
                "reproducible": {
                  "summary": "Auto-detected language, fixed seed, telephony μ-law",
                  "value": {
                    "format": "ulaw_8000",
                    "normalize": "on",
                    "seed": 42,
                    "text": "Ihre Bestellung ist unterwegs.",
                    "voice_id": "2b7f0c7e-4d2a-4b6e-9d0e-6f1c1a2b3c4d"
                  }
                }
              },
              "schema": {
                "$ref": "#/components/schemas/SpeechRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TimestampedSpeech"
                }
              },
              "audio/*": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              },
              "audio/mpeg": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              },
              "audio/wav": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "The synthesized audio. The media type follows `format` (`audio/wav`,\n`audio/mpeg`, raw PCM and μ-law/A-law frames under `audio/*`); with\n`timestamps=word` it is `application/json`.\n",
            "headers": {
              "X-Audio-Ms": {
                "$ref": "#/components/headers/X-Audio-Ms"
              },
              "X-Chars": {
                "$ref": "#/components/headers/X-Chars"
              },
              "X-Language": {
                "$ref": "#/components/headers/X-Language"
              },
              "X-Regeneration": {
                "$ref": "#/components/headers/X-Regeneration"
              },
              "X-Seed": {
                "$ref": "#/components/headers/X-Seed"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/StorageCapacity"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/StorageUnavailable"
          }
        },
        "summary": "Generate speech",
        "tags": [
          "tts"
        ],
        "x-required-scope": "tts"
      }
    },
    "/v1/tts/expressive": {
      "post": {
        "description": "Uses the separately deployed Qwen3-TTS 1.7B CustomVoice route. It never\naccepts clone embeddings. Style plus expanded tag instructions are at\nmost 500 Unicode characters; at most four whole-request tags may be\nsupplied. Inline and nonverbal events are unsupported. The ordinary TTS\ncharacter allowance, concurrency admission, output validation, and\ngeneration/usage records apply. Returns `501` when unavailable.\n",
        "operationId": "createExpressiveSpeech",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "format": {
                    "$ref": "#/components/schemas/Format"
                  },
                  "language": {
                    "$ref": "#/components/schemas/Language"
                  },
                  "speaker": {
                    "enum": [
                      "Ryan",
                      "Aiden",
                      "Vivian",
                      "Serena",
                      "Uncle_Fu",
                      "Dylan",
                      "Eric",
                      "Ono_Anna",
                      "Sohee"
                    ],
                    "type": "string"
                  },
                  "style": {
                    "maxLength": 500,
                    "type": "string"
                  },
                  "tags": {
                    "items": {
                      "enum": [
                        "happy",
                        "excited",
                        "sad",
                        "angry",
                        "calm",
                        "whisper",
                        "shout"
                      ],
                      "type": "string"
                    },
                    "maxItems": 4,
                    "type": "array"
                  },
                  "text": {
                    "maxLength": 10000,
                    "minLength": 1,
                    "type": "string"
                  }
                },
                "required": [
                  "text",
                  "speaker"
                ],
                "type": "object"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "audio/*": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "Generated expressive audio with ordinary TTS metering headers."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "501": {
            "$ref": "#/components/responses/NotImplemented"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        },
        "summary": "Generate instruction-controlled speech with a built-in speaker",
        "tags": [
          "tts"
        ],
        "x-required-scope": "tts"
      }
    },
    "/v1/tts/expressive/capabilities": {
      "get": {
        "description": "Returns `enabled: false` with empty speakers and tags when the deployment has no configured Qwen CustomVoice route. Clone style instructions, inline tags, and nonverbal audio tags are always false.",
        "operationId": "getExpressiveCapabilities",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "clone_style_instructions": {
                      "enum": [
                        false
                      ],
                      "type": "boolean"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "inline_tags": {
                      "enum": [
                        false
                      ],
                      "type": "boolean"
                    },
                    "nonverbal_audio_tags": {
                      "enum": [
                        false
                      ],
                      "type": "boolean"
                    },
                    "route": {
                      "enum": [
                        "custom_voice"
                      ],
                      "type": "string"
                    },
                    "speakers": {
                      "items": {
                        "type": "string"
                      },
                      "type": "array"
                    },
                    "style_instructions": {
                      "type": "boolean"
                    },
                    "tags": {
                      "items": {
                        "enum": [
                          "happy",
                          "excited",
                          "sad",
                          "angry",
                          "calm",
                          "whisper",
                          "shout"
                        ],
                        "type": "string"
                      },
                      "type": "array"
                    }
                  },
                  "required": [
                    "enabled",
                    "route",
                    "style_instructions",
                    "clone_style_instructions",
                    "inline_tags",
                    "nonverbal_audio_tags",
                    "tags",
                    "speakers"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Deployment-specific expressive capability contract."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "summary": "Discover available expressive controls",
        "tags": [
          "tts"
        ],
        "x-required-scope": "tts"
      }
    },
    "/v1/tts/stream": {
      "post": {
        "description": "Same body as `/v1/tts`, up to 20,000 characters (`timestamps` is not\nsupported). The text is split into sentences and each chunk is\nsynthesized in order, so the first audio arrives after roughly one\nsentence. The response is `text/event-stream`: one `data:` event per\nchunk (`StreamChunkEvent`), then a final `StreamDoneEvent`. A failure\nafter the first chunk ends the stream with a `StreamErrorEvent` — the\nHTTP status is already 200 by then. Only delivered chunks are metered.\n",
        "operationId": "streamSpeech",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SpeechRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "text/event-stream": {
                "example": "data: {\"seq\":1,\"audio_base64\":\"UklGR...\",\"duration_ms\":1810,\"format\":\"wav\"}\n\ndata: {\"seq\":2,\"audio_base64\":\"UklGR...\",\"duration_ms\":1420,\"format\":\"wav\"}\n\ndata: {\"done\":true,\"total_duration_ms\":3230,\"chars\":38}\n",
                "schema": {
                  "description": "Each `data:` payload is a JSON `StreamChunkEvent`, `StreamDoneEvent` or `StreamErrorEvent`.",
                  "type": "string"
                }
              }
            },
            "description": "Server-Sent Events, one `data:` line per event, events separated by a blank line."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        },
        "summary": "Stream speech as Server-Sent Events",
        "tags": [
          "tts"
        ],
        "x-required-scope": "tts"
      }
    },
    "/v1/tts/websocket": {
      "get": {
        "description": "Upgrade with the Bearer API key. Send one `start`, up to 128 `text`\nmessages totaling at most 20,000 Unicode characters, then `commit`.\nEach frame is at most 16 KiB. Ordered `audio` messages contain base64\naudio and end with `done`. `cancel` stops inference. Connections have a\nfive-minute read-idle and 15-second write deadline. A chunk is accepted\nfor quota checkpointing only after its socket write succeeds. This is\nsentence-chunked inference, not token-level model streaming: first audio\nfollows commit plus the first complete sentence inference.\n",
        "operationId": "streamSpeechWebSocket",
        "responses": {
          "101": {
            "description": "WebSocket protocol upgraded."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "summary": "Stream bounded text input and sentence audio over WebSocket",
        "tags": [
          "tts"
        ],
        "x-required-scope": "tts"
      }
    },
    "/v1/usage": {
      "get": {
        "description": "Existing speech-only totals for activity completed in the current calendar month (UTC) across every\nkey and portal of the account. Transcription does not increment these fields.\nInput-audio duration is reported separately in the portal Usage page and CSV.\nThe `storage` object is current retained and reserved capacity, not a monthly\nusage counter. `warning` becomes true at 80% of the plan's storage allowance.\nThe `limits` object gives current account entitlements; global safeguards\nstill apply. Byte allowances use decimal MB/GB.\nThese activity totals use completion dates. The `character_allowance` object attributes usage by admission time in the current plan window:\nFree receives 2,500 characters each week, resetting Monday 00:00 UTC;\nStarter receives 250,000 and Pro 1,000,000 per calendar month (UTC).\nUnused characters do not roll over. `used` includes settled usage, not pending\nreservations. Per-key character caps and transcription remain monthly.\nAdmission and completion periods can differ. Portal CSV includes both dates.\n",
        "operationId": "getUsage",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            },
            "description": "Monthly activity totals and current account allowances."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            },
            "description": "Current storage usage is unavailable; retry later."
          }
        },
        "summary": "Monthly activity and current character allowance",
        "tags": [
          "usage"
        ],
        "x-required-scope": "usage:read"
      }
    },
    "/v1/voices": {
      "get": {
        "description": "Your cloned, designed and marketplace voices plus the shared preset library (the `library` flag). Any `ready` voice works with `/v1/tts`.",
        "operationId": "listVoices",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VoiceList"
                }
              }
            },
            "description": "The voices available to the key's account."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        },
        "summary": "List voices",
        "tags": [
          "voices"
        ],
        "x-required-scope": "voices:read"
      },
      "post": {
        "description": "Multipart upload of a short speech sample (wav, mp3, m4a, mp4, ogg or\nwebm, up to 10 MB).\n`consent_attestation` must be the exact sentence *\"I confirm I have the\nright to clone this voice and consent to its use.\"* — it is stored with\nthe request IP as the consent record (see `/legal/voice-consent`). The\nvoice is `ready` when the response returns.\n",
        "operationId": "createVoice",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "encoding": {
                "sample": {
                  "contentType": "audio/wav, audio/mpeg, audio/mp4, audio/ogg, audio/webm"
                }
              },
              "schema": {
                "$ref": "#/components/schemas/VoiceCreateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Voice"
                }
              }
            },
            "description": "The new voice."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/StorageCapacity"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/StorageUnavailable"
          }
        },
        "summary": "Clone a voice from a sample",
        "tags": [
          "voices"
        ],
        "x-required-scope": "voices:write"
      }
    },
    "/v1/voices/design": {
      "post": {
        "description": "Synthesize a brand-new voice from a text description — no sample, no\nsource speaker. Servers where VoiceDesign is disabled answer `501`.\n",
        "operationId": "designVoice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VoiceDesignRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Voice"
                }
              }
            },
            "description": "The designed voice."
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/StorageCapacity"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "501": {
            "$ref": "#/components/responses/NotImplemented"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/StorageUnavailable"
          }
        },
        "summary": "Design a voice from a description",
        "tags": [
          "voices"
        ],
        "x-required-scope": "voices:write"
      }
    }
  },
  "security": [
    {
      "bearerKey": []
    }
  ],
  "servers": [
    {
      "description": "Era Voice",
      "url": "https://voice.erasmuslabs.ai"
    }
  ],
  "tags": [
    {
      "description": "Text-to-speech generation",
      "name": "tts"
    },
    {
      "description": "Your cloned and designed voices plus the preset library",
      "name": "voices"
    },
    {
      "description": "Stored generation audio",
      "name": "audio"
    },
    {
      "description": "Month-to-date usage",
      "name": "usage"
    },
    {
      "description": "Forced alignment (word timestamps for existing audio)",
      "name": "align"
    },
    {
      "description": "Asynchronous video/audio dubbing into another language",
      "name": "dubbing"
    },
    {
      "description": "Asynchronous transcription of uploaded recordings",
      "name": "transcription"
    }
  ]
}