{"openapi":"3.1.0","info":{"title":"VoiceWrap API","version":"1.0.0","description":"Text-to-speech in 11 languages (vi, en, zh, ja, ko, fr, de, es, it, pt, ru), each read by native voices; numbers, percentages and currency are spelled out in each language's conventions. Authenticate with `Authorization: Bearer sk_live_...` (create keys at https://voicewrap.app/app/keys). 1 credit = 1 character; failed jobs are refunded. MCP server: https://voicewrap.app/api/mcp. Agent guide: https://voicewrap.app/llms.txt.","termsOfService":"https://voicewrap.app/terms","contact":{"name":"VoiceWrap","url":"https://voicewrap.app"}},"externalDocs":{"description":"Full reference for AI agents","url":"https://voicewrap.app/llms-full.txt"},"servers":[{"url":"https://voicewrap.app"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"API key, sk_live_..."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}},"Job":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["queued","running","succeeded","failed"]},"progress":{"type":"number","description":"0 to 1 while running"},"queue_position":{"type":["integer","null"],"description":"Jobs ahead in the queue, if not started"},"voice_id":{"type":"string","format":"uuid"},"chars":{"type":"integer"},"cost":{"type":"integer","description":"Credits charged (1 per character)"},"format":{"type":"string","enum":["mp3","wav"]},"language":{"type":"string","description":"ISO 639-1 code the text was read in"},"audio_url":{"type":["string","null"],"description":"Signed URL to play, valid 1 hour"},"download_url":{"type":["string","null"],"description":"Signed URL that downloads the file, valid 1 hour"},"duration_seconds":{"type":["number","null"]},"error":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"finished_at":{"type":["string","null"],"format":"date-time"}}},"Voice":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"key":{"type":"string","description":"Accent-free slug, also accepted as `voice`"},"description":{"type":["string","null"],"description":"Gender · accent · style"},"type":{"type":"string","enum":["system","cloned"]},"language":{"type":["string","null"],"description":"ISO 639-1 language a built-in voice reads (only that one); null for cloned voices, which read any"},"gender":{"type":["string","null"],"enum":["female","male",null]},"preview_url":{"type":["string","null"]}}}}},"paths":{"/api/v1/audio/speech":{"post":{"operationId":"createSpeech","summary":"OpenAI-compatible: returns the audio file directly","description":"Drop-in for OpenAI's POST /v1/audio/speech: point an OpenAI SDK's base_url at https://voicewrap.app/api/v1. Waits for long text (up to ~4.5 minutes).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["input"],"properties":{"input":{"type":"string","maxLength":5000},"voice":{"type":"string","description":"Voice name (accents and case optional, e.g. \"Ngọc Huyền\", \"ngoc-huyen\" or \"Emma\"), voice id, or an OpenAI voice name (alloy, ash, ballad, coral, echo, fable, nova, onyx, sage, shimmer, verse). Each built-in voice is a native speaker and reads only its own language, which it sets; cloned voices read any supported language. OpenAI names map to a native voice of the same gender in the request's language (fixed voices for Vietnamese). Default: the language's default voice (vi: Hải Đăng, en: Emma, zh: Vivian, ja: Anna, ko: Sohee, fr: Camille, de: Lena, es: Lucía, it: Giulia, pt: Beatriz, ru: Natasha). See GET /api/v1/voices."},"response_format":{"type":"string","enum":["mp3","wav"],"default":"mp3"},"speed":{"type":"number","minimum":0.25,"maximum":4,"default":1,"description":"Clamped to 0.5–2"},"language":{"type":"string","enum":["vi","en","zh","ja","ko","fr","de","es","it","pt","ru"],"description":"ISO 639-1 language to read the text in. Default: a built-in voice's own language; with a cloned voice or no voice, detected from the text. A built-in voice reads only its own language: a different value returns 400 voice_language_mismatch. A code (or detected language) outside this list returns 400 unsupported_language."},"model":{"type":"string","description":"Ignored"}}}}}},"responses":{"200":{"description":"Audio bytes","headers":{"x-voicewrap-job-id":{"schema":{"type":"string"}},"x-voicewrap-cost":{"schema":{"type":"integer"}}},"content":{"audio/mpeg":{"schema":{"type":"string","format":"binary"}},"audio/wav":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid request (invalid_request), unsupported_language, or voice_language_mismatch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Not enough credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Voice or job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (30 requests/minute)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Synthesis failed; credits refunded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"504":{"description":"Still running; poll GET /api/v1/tts/{id}"}}}},"/api/v1/tts":{"post":{"operationId":"createTtsJob","summary":"Create speech; returns a job with a signed audio URL","description":"Text up to 500 characters returns 200 with the finished job. Longer text (or async=true) returns 202; poll GET /api/v1/tts/{id}.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text"],"properties":{"text":{"type":"string","maxLength":5000},"voice":{"type":"string","description":"Voice name (accents and case optional, e.g. \"Ngọc Huyền\", \"ngoc-huyen\" or \"Emma\"), voice id, or an OpenAI voice name (alloy, ash, ballad, coral, echo, fable, nova, onyx, sage, shimmer, verse). Each built-in voice is a native speaker and reads only its own language, which it sets; cloned voices read any supported language. OpenAI names map to a native voice of the same gender in the request's language (fixed voices for Vietnamese). Default: the language's default voice (vi: Hải Đăng, en: Emma, zh: Vivian, ja: Anna, ko: Sohee, fr: Camille, de: Lena, es: Lucía, it: Giulia, pt: Beatriz, ru: Natasha). See GET /api/v1/voices."},"speed":{"type":"number","minimum":0.5,"maximum":2,"default":1},"format":{"type":"string","enum":["mp3","wav"],"default":"mp3"},"language":{"type":"string","enum":["vi","en","zh","ja","ko","fr","de","es","it","pt","ru"],"description":"ISO 639-1 language to read the text in. Default: a built-in voice's own language; with a cloned voice or no voice, detected from the text. A built-in voice reads only its own language: a different value returns 400 voice_language_mismatch. A code (or detected language) outside this list returns 400 unsupported_language."},"async":{"type":"boolean","description":"Force background processing"}}}}}},"responses":{"200":{"description":"Finished job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"202":{"description":"Running job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"400":{"description":"Invalid request (invalid_request), unsupported_language, or voice_language_mismatch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Not enough credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Voice or job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (30 requests/minute)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Synthesis failed; credits refunded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/tts/{id}":{"get":{"operationId":"getTtsJob","summary":"Job status and audio URLs","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"400":{"description":"Invalid request (invalid_request), unsupported_language, or voice_language_mismatch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Not enough credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Voice or job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (30 requests/minute)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Synthesis failed; credits refunded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/voices":{"get":{"operationId":"listVoices","summary":"System voices and your cloned voices","parameters":[{"name":"language","in":"query","required":false,"schema":{"type":"string","enum":["vi","en","zh","ja","ko","fr","de","es","it","pt","ru"]},"description":"Only the built-in voices that read this language, plus your cloned voices"}],"responses":{"200":{"description":"Voices","content":{"application/json":{"schema":{"type":"object","properties":{"voices":{"type":"array","items":{"$ref":"#/components/schemas/Voice"}}}}}}},"400":{"description":"Invalid request (invalid_request), unsupported_language, or voice_language_mismatch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Not enough credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Voice or job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (30 requests/minute)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Synthesis failed; credits refunded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"cloneVoice","summary":"Clone a voice from 3–15 s of speech you have the right to use","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["name","ref_text","consent","file"],"properties":{"name":{"type":"string","maxLength":60},"ref_text":{"type":"string","description":"Exact transcript of the clip"},"consent":{"type":"string","enum":["true"]},"file":{"type":"string","format":"binary"}}}}}},"responses":{"201":{"description":"Created voice"},"400":{"description":"Invalid request (invalid_request), unsupported_language, or voice_language_mismatch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Not enough credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Voice or job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (30 requests/minute)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Synthesis failed; credits refunded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/me":{"get":{"operationId":"getAccount","summary":"Account and credit balance","responses":{"200":{"description":"Account","content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"credits_balance":{"type":"integer"}}}}}},"400":{"description":"Invalid request (invalid_request), unsupported_language, or voice_language_mismatch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Not enough credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Voice or job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (30 requests/minute)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Synthesis failed; credits refunded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}