TypeScript SDK
Also available for Python and Go, with the same methods.
@vovix/voixa wraps every endpoint with types, waits for clips and transcripts for you, and has no dependencies. It runs in Node 18+, Bun, Deno, Cloudflare Workers and the browser.
Install
npm
npm install @vovix/voixa
Create a client
import { Voixa } from '@vovix/voixa'
const vx = new Voixa({
apiKey: process.env.VOIXA_API_KEY!, // Studio → API keys
// baseUrl: 'https://api.voixa.vovix.io/v1', (default)
// timeoutMs: 30_000, (per HTTP request)
})
Keep the key on your server. In a browser, create the client with idToken (a signed-in Studio session) instead of apiKey — exactly one of the two.
A tour
// Built-in voices and your clones
const { voices } = await vx.voices({ language: 'vi' })
// Text → clip. say() waits until the audio is ready; speak() returns at once.
const clip = await vx.say({ voiceId: 'vi-truc-ly', text: 'Xin chào.', project: 'episode-12' })
clip.url // signed WAV URL, valid for one hour
// Long text → one audio file
const episode = await vx.makePodcast({ voiceId: 'vi-truc-ly', title: 'Episode 12', text: script })
// Your library, grouped by project
const { items } = await vx.listClips({ project: 'episode-12', source: 'api' })
const { shareUrl } = await vx.shareClip(clip.clipId)
// Clone a voice from 3–10 s of clean speech
const me = await vx.createVoice({ name: 'Me', language: 'vi', audio: wavBytes })
await vx.say({ voiceId: me.voiceId, text: 'Giờ là giọng của tôi.' })
// Speech to text with timestamps
const t = await vx.transcribeFile({ audio: await fs.readFile('interview.mp3'), fileName: 'interview.mp3' })
t.text, t.segments, t.srtUrl
Methods
Speech
| Method | Returns |
|---|---|
speak({ voiceId, text, project?, wait? }) | { clip } as the server has it after its ~20 s wait. wait: false returns at once with status: "processing". |
say(req, { timeoutMs?, pollMs? }) | speak plus polling until the clip is ready; throws if it fails. |
createPodcast({ voiceId, title?, text, project? }) | { clip } with kind: "podcast", always processing at first. |
makePodcast(req, wait?) | createPodcast plus polling, up to two hours. |
Library
| Method | Returns |
|---|---|
getClip(clipId), waitFor(clipId) | One clip with a fresh signed URL; poll until ready. |
listClips({ project?, source?, kind?, voiceId?, limit?, cursor? }) | Your clips, newest first; cursor for the next page. |
projects() | Your content groups with clip and character counts. |
shareClip(clipId), unshareClip(clipId) | A public listening page (shareUrl), and revoking it. |
deleteClips(clipIds) | { deleted }. |
Voices
| Method | Returns |
|---|---|
voices({ language? }), getVoice(voiceId) | Built-in voices, your clones and clones others shared. |
samples({ language? }) | Built-in voices with a public sample URL, for a picker in your app. |
createVoice({ name, language, audio, format?, gender?, description?, refText? }) | Uploads the recording and waits for the clone and its sample. |
updateVoice(voiceId, { name?, description?, gender?, styles?, shared? }) | shared: true lets every Voixa account use your clone. |
deleteVoice(voiceId) | Deletes the clone and its recording; clips made with it stay. |
Voice design
| Method | Returns |
|---|---|
designVoice({ description, language, text?, count? }) | { design } with status: "processing"; 1–3 takes of the described voice. |
waitForDesign(designId), getDesign(designId) | The design with a signed url per take once ready. |
saveDesign(designId, { candidate, name, gender?, description? }) | Keeps one take as a voice and waits for its sample. |
createVoiceFromDescription({ description, language, name, gender?, text? }) | One call: design one take, wait, keep it. |
listDesigns({ limit?, cursor? }), deleteDesign(designId) | Your designs, newest first; deleting keeps saved voices. |
Transcription
| Method | Returns |
|---|---|
transcribe({ audio, fileName?, title?, language?, task?, words?, prompt?, project? }) | Registers and uploads in one go; { transcript } with status: "processing". |
transcribeFile(req, wait?) | transcribe plus polling until ready. |
getTranscript(id), waitForTranscript(id) | Text, segments and signed JSON/SRT/VTT/TXT URLs. |
listTranscripts({ project?, source?, status?, limit?, cursor? }), deleteTranscripts(ids) | Summaries without text; delete also removes the upload. |
sttLanguages() | Languages you may pin, accepted formats and limits. |
Account
| Method | Returns |
|---|---|
account() | Allowances and today's usage. |
languages() | Languages with limits, cloneable and a clone-recording guide. |
Errors
Every failed call throws VoixaError with status, code, retryAfter (seconds, from Retry-After) and, for allowances, used, limit and resetsAt. See Errors.
import { VoixaError } from '@vovix/voixa'
try {
await vx.say({ voiceId: 'vi-truc-ly', text })
} catch (e) {
if (e instanceof VoixaError && e.code === 'QUOTA') console.log('Allowance resets at', e.resetsAt)
else throw e
}