# Iodine API documentation > Lifelike text-to-speech over HTTP and WebSocket. Base URL: https://api.iodine.dev # Introduction Source: https://docs.iodine.dev/ Iodine turns text into lifelike speech over a simple HTTP API. Audio streams back while it's being generated, so playback can start almost immediately, and you pay only for the minutes of audio you get. - **Base URL:** `https://api.iodine.dev` - **Authentication:** an API key in the `Authorization` header - **Output:** 24 kHz mono 16-bit audio, as WAV or raw PCM - **Price:** $0.0075 per minute of speech generated, $0.00015 per minute transcribed, from prepaid credit ## Models | Model | What it's for | |---|---| | `v1` | The production model: fast, natural and steady. Voices `ava`, `katie`, `sophie`. | | `v2` | The expressive model: six languages, [emotion and delivery cues](https://docs.iodine.dev/directions), and [cloned voices](https://docs.iodine.dev/voices#cloning). Voices `mia`, `chloe`, `zander`, `ethan`. | You choose a model with the `model` field, or leave it out and Iodine uses the model that has the voice you asked for. See [Voices](https://docs.iodine.dev/voices). ## Quickstart ### 1. Get an API key Sign in to the [console](https://iodine.dev/console), add credit under **Billing**, then create a key under **API keys**. Keys look like `io_live_…` and are shown once, so store yours somewhere safe. Keep it on your server: anyone with the key can spend your credit. ```bash export IODINE_API_KEY="io_live_..." ``` ### 2. Make a request ```bash curl https://api.iodine.dev/v1/tts \ -H "Authorization: Bearer $IODINE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"text": "Hello from Iodine.", "voice": "katie"}' \ --output hello.wav ``` That's it: `hello.wav` holds the speech. ### Python ```python import os import requests resp = requests.post( "https://api.iodine.dev/v1/tts", headers={"Authorization": f"Bearer {os.environ['IODINE_API_KEY']}"}, json={"text": "Hello from Iodine.", "voice": "katie"}, stream=True, ) resp.raise_for_status() with open("hello.wav", "wb") as f: for chunk in resp.iter_content(chunk_size=None): f.write(chunk) ``` ### JavaScript (Node 18+) ```js import { writeFile } from "node:fs/promises"; const resp = await fetch("https://api.iodine.dev/v1/tts", { method: "POST", headers: { Authorization: `Bearer ${process.env.IODINE_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ text: "Hello from Iodine.", voice: "katie" }), }); if (!resp.ok) throw new Error((await resp.json()).error.message); await writeFile("hello.wav", Buffer.from(await resp.arrayBuffer())); ``` ## Next steps - [Text to speech](https://docs.iodine.dev/text-to-speech): every request option, streaming and the audio formats. - [Emotion and delivery](https://docs.iodine.dev/directions): direct a `v2` voice with `{happy}`, `{sighs}`, `{whisper}…{/whisper}`. - [Realtime](https://docs.iodine.dev/realtime): a WebSocket for voice agents that speaks text as an LLM writes it. - [Voices](https://docs.iodine.dev/voices): list voices and clone your own. - [Speech to text](https://docs.iodine.dev/speech-to-text): transcribe recordings, OpenAI-compatible. - [Errors and limits](https://docs.iodine.dev/errors): what can go wrong and how to handle it. - [Billing and data](https://docs.iodine.dev/billing): credits, pricing and Zero Data Retention. --- # Text to speech Source: https://docs.iodine.dev/text-to-speech ```http POST https://api.iodine.dev/v1/tts Authorization: Bearer io_live_... Content-Type: application/json ``` ## Request body | Field | Type | Default | | |---|---|---|---| | `text` | string | required | What to say. Up to 5,000 characters. On `v2`, may include [emotion and delivery cues](https://docs.iodine.dev/directions) like `{happy}`. Line breaks, tabs and repeated spaces are tidied up for you. | | `voice` | string | required | A voice id from [GET /v1/voices](https://docs.iodine.dev/voices), or one of your cloned voices (`vc_…`). | | `model` | `"v1"` \| `"v2"` | from the voice | Which model speaks. Left out, Iodine picks the model that has the voice, `v1` first. | | `format` | `"wav"` \| `"pcm"` | `"wav"` | WAV with a header, or raw PCM samples. | | `stream` | boolean | `true` | Stream audio as it's generated, or send it all at once when it's done. | | `temperature` | number | the model's | Between 0 and 2. Lower is steadier, higher is more varied. | | `language` | `"en"` \| `"es"` \| `"fr"` \| `"de"` \| `"ja"` \| `"zh"` | from the text | `v2` only: the language you're speaking, for the most natural accent. Japanese and Chinese are recognised from the text. | ```json { "text": "Your order has shipped and should arrive on Thursday.", "voice": "katie", "format": "wav", "stream": true } ``` ## Response On success the body is audio, with these headers: | Header | Value | |---|---| | `Content-Type` | `audio/wav` or `audio/pcm` | | `X-Request-Id` | The request's id. It also appears in the console's request log, so quote it when you contact us. | | `X-Sample-Rate` | `24000` | | `X-Audio-Encoding` | `s16le` (signed 16-bit, little-endian) | | `X-Audio-Channels` | `1` | On failure the body is JSON: `{"error": {"code": "...", "message": "..."}}`. See [Errors and limits](https://docs.iodine.dev/errors). ## Streaming By default (`"stream": true`) audio is sent as soon as each piece is generated, so you can start playing it before the whole sentence is done. With `"format": "wav"` the stream begins with a WAV header whose length fields are set to the maximum, as is usual for streamed WAV. Players and libraries handle this, but if you need exact lengths, either set `"stream": false` or ask for `pcm` and add your own header. With `"stream": false` the response arrives in one piece once generation finishes, with a `Content-Length` and, for WAV, an exact header. ## Formats - **`wav`**: plays anywhere. 24 kHz, mono, 16-bit PCM inside a WAV container. - **`pcm`**: raw samples, the same audio without a header. Best for piping into a player, a phone line or your own encoder. Play raw PCM with ffplay: ```bash curl -s https://api.iodine.dev/v1/tts \ -H "Authorization: Bearer $IODINE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"text": "Streaming straight to the speakers.", "voice": "katie", "format": "pcm"}' \ | ffplay -f s16le -ar 24000 -ch_layout mono -nodisp -autoexit - ``` ## Playing a stream in Python ```python import os import requests import sounddevice as sd # pip install sounddevice with requests.post( "https://api.iodine.dev/v1/tts", headers={"Authorization": f"Bearer {os.environ['IODINE_API_KEY']}"}, json={"text": "Playback starts before generation ends.", "voice": "katie", "format": "pcm"}, stream=True, ) as resp, sd.RawOutputStream(samplerate=24000, channels=1, dtype="int16") as out: resp.raise_for_status() leftover = b"" for chunk in resp.iter_content(chunk_size=4096): data = leftover + chunk cut = len(data) - len(data) % 2 # whole 16-bit samples only out.write(data[:cut]) leftover = data[cut:] ``` ## What you're charged You pay for the audio you receive: its length in minutes times your rate. If you hang up partway through a stream, you pay only for what was sent. Requests that fail on our side cost nothing. See [Billing and data](https://docs.iodine.dev/billing). --- # Emotion and delivery Source: https://docs.iodine.dev/directions Write cues in `{curly braces}` inside your `text`. They're never spoken: they tell the voice *how* to say what follows. ```json { "text": "{sad} I didn't think you'd come back. {sighs} I'm glad you did. {whisper}Don't tell anyone.{/whisper}", "voice": "mia", "model": "v2" } ``` Cues work on `v2`. On `v1` they're removed before speaking, except `{laughs}`, which `v1` can perform. ## Emotion Put an emotion at the start of a sentence and it colours what follows. Change it at the next sentence. | Cue | Sounds like | |---|---| | `{happy}` | warm and bright, smiling | | `{sad}` | heavy, close to tears | | `{angry}` | frustrated, sharp | | `{scared}` | nervous, shaking | | `{excited}` | energetic, can't hold it in | | `{calm}` | gentle, unhurried | | `{nervous}` | hesitant, unsure | | `{tired}` | low energy, drawn out | | `{playful}` | teasing, light | | `{serious}` | steady, measured | | `{warm}` | friendly, close | | `{sarcastic}` | dry, deadpan | ## Any direction you can describe You aren't limited to the list. Describe the delivery in plain words, the way you'd brief an actor: ```text {barely holding it together, forcing a smile} I'm fine. Really. {low, gravelly, tired but in control} Status report. {fast, stumbling over the words} I didn't mean to, I swear, it just happened. ``` Short, concrete descriptions work best. Combine ideas with commas. ## Sounds and pauses Place a sound exactly where it should happen: ```text I'm fine. {shaky breath} Really. {small laugh} Go on without me. ``` `{laughs}` `{giggles}` `{sighs}` `{gasps}` `{sniffles}` `{sobs}` `{coughs}` `{clears throat}` `{shaky breath}` all work, as does any other short description of a sound. For silence, use `{pause}` or `{long pause}`. ## Styles for a few words Wrap words in a style and close it with a slash. Only the enclosed words change: ```text It's going to {loud}blow{/loud}. {whisper}Get down.{/whisper} ``` | Style | Effect | |---|---| | `{whisper}…{/whisper}` | whispered | | `{soft}…{/soft}` | quieter, gentler | | `{loud}…{/loud}` | louder, forceful | | `{emphasis}…{/emphasis}` | stressed | | `{fast}…{/fast}` | quicker | | `{slow}…{/slow}` | slower | A style you never close applies as a direction for what follows instead, so `{whisper} Come closer.` is whispered too. ## Languages `v2` voices speak English, Spanish, French, German, Japanese and Chinese: write your text in the language you want. Japanese and Chinese are recognised from the text; for Spanish, French and German, also send `"language": "es"` (or `fr`, `de`) for the most natural accent. Cues themselves can be written in any language. ## Tips - One emotion per sentence reads most naturally. Change it between sentences, not mid-word. - Keep to two or three cues in a row; more tends to blur them. - On the [realtime socket](https://docs.iodine.dev/realtime), text is spoken a sentence at a time, so keep a style's `{…}` and `{/…}` within one sentence. - Square brackets and angle brackets in your text are read as ordinary text, never as instructions: `[` and `]` become parentheses and `<` `>` are dropped. --- # Realtime Source: https://docs.iodine.dev/realtime `POST /v1/tts` needs the whole text up front. A voice agent doesn't have it: the reply arrives from the LLM a few tokens at a time. The realtime endpoint takes text as it's written and speaks each sentence as soon as it's complete, so the caller hears the first words while the rest of the reply is still being generated. It's a plain WebSocket: connect from your server the same way a LiveKit or Pipecat agent connects to any streaming TTS. No WebRTC is involved. ## Connect ```text GET wss://api.iodine.dev/v1/tts/realtime?voice=katie Authorization: Bearer io_live_... ``` | Query parameter | | | |---|---|---| | `voice` | required | A voice id, as for `POST /v1/tts`. | | `model` | optional | `v1` or `v2`. Left out, Iodine picks the model that has the voice. | | `temperature` | optional | Between 0 and 2. | | `language` | optional | `v2`: `en`, `es`, `fr`, `de`, `ja` or `zh`, as for `POST /v1/tts`. | The key goes in the `Authorization` header, so connect from your backend; browsers can't set headers on a WebSocket, and your key must never reach a browser anyway. A bad key, an unknown voice or an empty balance is refused with a normal HTTP error before the socket opens. ## Messages you send JSON text messages: | Message | Effect | |---|---| | `{"type": "text", "text": "Hi there, "}` | Add text. Complete sentences are spoken as they form. | | `{"type": "text", "text": "…", "flush": true}` | Add text and speak everything buffered now. | | `{"type": "flush"}` | Speak whatever is buffered now, even mid-sentence. Send it when the LLM's turn ends. | | `{"type": "cancel"}` | Stop speaking at once and drop buffered text. Use it when the user interrupts. | | `{"type": "close"}` | Speak what's left, send `done`, then close the socket. | ## Messages you receive - **Binary messages:** raw audio, 24 kHz mono signed 16-bit little-endian PCM. Play them in order as they arrive. - **JSON text messages:** | Event | When | |---|---| | `{"type": "ready", "voice": "katie", "model": "v2", "sampleRate": 24000, "encoding": "pcm_s16le", "channels": 1}` | Once, when the socket is ready for text. | | `{"type": "segment", "id": "…", "chars": 42}` | A piece of speech is starting. `id` is its request id. | | `{"type": "segment_done", "id": "…", "audioMs": 2310, "ms": 980}` | That piece is finished: audio length and time taken. | | `{"type": "cancelled"}` | Your `cancel` took effect. | | `{"type": "error", "code": "…", "message": "…"}` | Something went wrong, for example `insufficient_credits` for the next piece. | | `{"type": "done"}` | After `close`, once everything has been spoken. | ## How text is split Text is spoken a sentence at a time. A sentence boundary only ends a piece once it holds at least 12 characters, so a lone "Hi." waits for the next sentence. Text with no sentence boundary is cut at a space once it reaches 300 characters. `flush` speaks whatever is buffered, however short. Each piece is billed as one request, by the length of its audio, at the same rate as `POST /v1/tts`. ## Example (Python) ```python import asyncio, json, os import websockets # pip install websockets async def speak(chunks): async with websockets.connect( "wss://api.iodine.dev/v1/tts/realtime?voice=katie", additional_headers={"Authorization": f"Bearer {os.environ['IODINE_API_KEY']}"}, ) as ws: print(await ws.recv()) # ready for chunk in chunks: # e.g. tokens from your LLM await ws.send(json.dumps({"type": "text", "text": chunk})) await ws.send(json.dumps({"type": "close"})) with open("reply.pcm", "wb") as f: async for msg in ws: if isinstance(msg, bytes): f.write(msg) # play it, or forward it to your call elif json.loads(msg)["type"] == "done": break asyncio.run(speak(["Sure! ", "Your order shipped this morning ", "and should arrive on Thursday."])) ``` ## Limits - A socket with no message from you for 5 minutes is closed (`idle_timeout`). - One message can be up to 256 KB. - An `error` about one piece doesn't close the socket; keep sending, or `close` it yourself. --- # Voices Source: https://docs.iodine.dev/voices ## List voices ```http GET https://api.iodine.dev/v1/voices Authorization: Bearer io_live_... ``` ```json { "voices": [ { "id": "vc_4fK9zQ2mXp7LrT1a", "model": "v2", "name": "Narrator (Sam)", "custom": true, "durationSeconds": 18.4, "createdAt": "2026-11-09T12:00:00Z" }, { "id": "mia", "model": "v2", "languages": ["en", "es", "fr", "de", "ja", "zh"] }, { "id": "katie", "model": "v1" } ] } ``` Your account's cloned voices come first, then the shared voices. Each entry says which model speaks it, and `v2` voices list the languages they speak. Use the `id` as `voice` in a request. Voices are added over time, so read the list rather than hard-coding it; the console's playground lets you hear every voice. ## Voices | Voice | Model | Character | |---|---|---| | `ava` | `v1` | Warm, natural and expressive, for conversational agents. | | `katie` | `v1` | Clear and reassuring, for support lines and service. | | `sophie` | `v1` | Bright and energetic, for games and interactive apps. | | `mia` | `v2` | Soft, calm and close, for wellness and coaching. | | `chloe` | `v2` | Quick, chatty and fun, for social apps and entertainment. | | `zander` | `v2` | Low, warm and expressive, for audiobooks and narration. | | `ethan` | `v2` | Gentle and steady, for healthcare and onboarding. | `v2` voices speak English, Spanish, French, German, Japanese and Chinese, and take [emotion and delivery cues](https://docs.iodine.dev/directions). ## Cloning Clone a voice from a 3 to 30 second recording of one person speaking. Cloned voices are spoken by `v2`, are private to your account (nobody else can see or use them) and are never used for training. ```bash curl https://api.iodine.dev/v1/voices \ -H "Authorization: Bearer $IODINE_API_KEY" \ -F name="Narrator (Sam)" \ -F file=@sam.wav \ -F consent=true ``` Multipart form fields: | Field | | | |---|---|---| | `file` | required | The recording: WAV, MP3, FLAC or OGG, 3 to 30 seconds, up to 20 MB. 10 to 30 seconds of clear speech with no music or other voices works best. | | `name` | required | 1 to 64 characters, for your own reference. | | `consent` | required | `true`. You confirm that the speaker has given permission for their voice to be cloned. We record which key confirmed it. | | `transcript` | optional | What's said in the recording, up to 2,000 characters. Left out, we transcribe it for you. An accurate transcript gives a closer clone. | The response is `201 Created`: ```json { "voice": { "id": "vc_4fK9zQ2mXp7LrT1a", "model": "v2", "name": "Narrator (Sam)", "custom": true, "durationSeconds": 18.4, "createdAt": "2026-11-09T12:00:00Z", "transcript": "Hi, I'm Sam, and this is how I sound when I read.", "transcriptSource": "auto" } } ``` `transcriptSource` is `provided`, `auto` (we transcribed it) or `none` (no transcript could be made; the voice still works, just less closely). Use the new `id` straight away: ```bash curl https://api.iodine.dev/v1/tts \ -H "Authorization: Bearer $IODINE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"text": "Chapter one.", "voice": "vc_4fK9zQ2mXp7LrT1a"}' \ --output chapter-1.wav ``` An account can keep up to 50 cloned voices. ### Only clone voices you have permission to use Clone a voice only if you are the speaker, or the speaker has given you permission to clone it and to use it as you intend; you must be able to show that permission if we ask. Don't use any voice to impersonate a real person without their consent, to mislead anyone about who is speaking, or to commit fraud. We may delete a cloned voice, and suspend the account that made it, if it breaks these rules. See the [Terms](https://iodine.dev/legal/terms#voice-cloning). ## Delete a cloned voice ```http DELETE https://api.iodine.dev/v1/voices/vc_4fK9zQ2mXp7LrT1a Authorization: Bearer io_live_... ``` Returns `204 No Content`. The recording is deleted and the id stops working at once. --- # Speech to text Source: https://docs.iodine.dev/speech-to-text ```bash curl https://api.iodine.dev/v1/stt \ -H "Authorization: Bearer $IODINE_API_KEY" \ -F file=@call.mp3 \ -F language=en ``` ```json { "text": "Hi, I'm calling about my order from last week.", "language": "en", "duration": 4.2 } ``` ## Request Multipart form fields: | Field | | | |---|---|---| | `file` | required | The recording: WAV, MP3, FLAC or OGG, up to 25 MB and 10 minutes. Split longer recordings. | | `language` | optional | The language spoken, as a code like `en`, `es`, `ja` or `pt`. Default `en`. Set it for anything that isn't English: the audio is transcribed in this language. | | `response_format` | optional | `json` (default) or `text` for the transcript alone, as plain text. | The response has an `X-Request-Id` header, like every request. ## With OpenAI's SDK `/v1/audio/transcriptions` is the same endpoint at OpenAI's path, and fields we don't use (`model`, `prompt`) are ignored, so existing code needs only a new base URL and key: ```python import os from openai import OpenAI client = OpenAI(base_url="https://api.iodine.dev/v1", api_key=os.environ["IODINE_API_KEY"]) with open("call.mp3", "rb") as f: result = client.audio.transcriptions.create(model="whisper-1", file=f, language="en") print(result.text) ``` ## Pricing **$0.00015 per minute of audio** ($0.009 an hour), measured from the recording you send, to the millionth of a dollar. Requests that fail on our side cost nothing. It draws on the same credit balance as text to speech, and shows in the console's usage as `stt`. ## Your audio Recordings are transcribed in memory and never stored, whatever your Zero Data Retention setting; we keep only the request's metadata (length, language, cost). See [Billing and data](https://docs.iodine.dev/billing). --- # Errors and limits Source: https://docs.iodine.dev/errors Errors come back with an HTTP status and a JSON body: ```json { "error": { "code": "unknown_voice", "message": "Voice `kate` isn't available for model `v1`. See GET /v1/voices." } } ``` Branch on `code`, which stays the same; `message` is written for people and may change. ## Error codes | Status | Code | Meaning | What to do | |---|---|---|---| | 400 | `invalid_request` | The body isn't valid JSON or has the wrong shape. | Fix the request; the message says what's wrong. | | 400 | `empty_text` | `text` is empty, or only whitespace. | Send some text. | | 400 | `text_too_long` | `text` is over 5,000 characters. | Split it into several requests, ideally at sentence boundaries. | | 400 | `unknown_voice` | No such voice for that model, or a cloned voice from another account. | Check the id with `GET /v1/voices`. | | 400 | `invalid_temperature` | `temperature` is outside 0 to 2. | Use a value in range, or leave it out. | | 400 | `invalid_language` | `language` isn't one of `en`, `es`, `fr`, `de`, `ja`, `zh`. | Use one of those, or leave it out. | | 401 | `unauthorized` | The API key is missing, wrong or revoked. | Send `Authorization: Bearer io_live_…` with a current key. | | 402 | `insufficient_credits` | Your credit balance is used up. | Add credit or turn on auto-recharge in [Billing](https://iodine.dev/console/billing). | | 404 | `not_found` | No such endpoint, or a cloned voice that doesn't exist. | Check the path and id. | | 502 | `upstream_error`, `upstream_unreachable` | Generation failed on our side. You aren't charged. | Retry with backoff. | | 503 | `busy` | Every slot for that model was taken for 3 seconds. | Retry with backoff. | | 500 | `internal` | Something broke on our side. | Retry; if it keeps happening, tell us the `X-Request-Id`. | Speech to text adds `invalid_response_format` and, like cloning, `missing_audio`, `invalid_audio` and `audio_too_long`. Cloning adds its own codes, all 400: `consent_required`, `invalid_name`, `invalid_transcript`, `missing_audio`, `invalid_audio`, `audio_too_short`, `audio_too_long` and `too_many_voices`. Each message says exactly what to fix. ## Retrying Retry `502`, `503` and `500` with exponential backoff and jitter, for example after 0.5 s, 1 s, 2 s and 4 s. Don't retry `4xx` errors unchanged: they fail the same way every time. ```python import random, time, requests def tts(payload, key, attempts=5): for i in range(attempts): r = requests.post("https://api.iodine.dev/v1/tts", json=payload, headers={"Authorization": f"Bearer {key}"}) if r.status_code not in (500, 502, 503): r.raise_for_status() return r.content time.sleep(0.5 * 2 ** i + random.random() * 0.25) r.raise_for_status() ``` ## Limits | | | |---|---| | Text per request | 5,000 characters | | Waiting for a free slot | up to 3 seconds, then `503 busy` | | Realtime idle timeout | 5 minutes without a message | | Realtime message size | 256 KB | | Cloned voice recording | 3 to 30 seconds, up to 20 MB | | Cloned voices per account | 50 | | Speech to text upload | 25 MB, 10 minutes | Need more throughput or higher limits? Email [business@iodine.dev](mailto:business@iodine.dev). --- # Billing and data Source: https://docs.iodine.dev/billing ## Pricing **$0.0075 per minute of audio**, for both models. [Speech to text](https://docs.iodine.dev/speech-to-text) is **$0.00015 per minute** of recording ($0.009 an hour). You're charged for the length of the audio you receive, to the millionth of a dollar, so a 10-second clip costs $0.00125. There are no plans, seats or minimums. - Requests that fail on our side cost nothing. - If you disconnect partway through a stream, you pay only for the audio that was sent. - Each realtime piece is billed the same way, as its own request. ## Credits Usage draws down a prepaid balance in US dollars. - **Top up** in the console under [Billing](https://iodine.dev/console/billing): $5 to $5,000 at a time, by card, with a receipt for each. - **Auto-recharge** tops you up when the balance falls below an amount you choose, using your saved card, so your app never stops. - **At zero**, requests fail with `402 insufficient_credits` until you add credit. Requests already running finish. The console shows your balance, every top-up, and your usage and spend by day. Receipts, invoices, your card and your billing details (address, tax ID) are in the billing portal, linked from the Billing page. **High volume or invoicing?** We offer custom rates and monthly invoicing with payment terms. Email [business@iodine.dev](mailto:business@iodine.dev). ## Zero Data Retention Zero Data Retention (ZDR) is on for every account by default, at no extra cost: - The text you send is held in memory only while the audio is generated, then discarded. - Generated audio is streamed to you and never stored. Responses are marked `no-store`. - We keep only request metadata (when, which key, model and voice, the character count but not the characters, audio length, time to first byte and status), which powers your usage page and billing. The account owner can turn ZDR off in the console's settings. While it's off, we keep the text and audio of each completed request and may use them to train and improve our models, and those requests cost **15% less: $0.006375 per minute** at the list price. Turning it back on takes effect immediately for new requests, and the owner can delete everything kept from the console. Cloned voices are never used for training, whatever the setting. The full details are in the [Privacy Policy](https://iodine.dev/legal/privacy).