Quickstart
From a fresh key to playable audio in about a minute. Every snippet is complete: paste it, set your key, run it.
1. Get a key
Create a key in the dashboard. New accounts start with 100,000 free characters, so nothing on this page costs anything. Export it once and every sample below runs as written:
export QUICKVOICE_KEY="qtts_live_xxxxxxxxxxxxxxxxxxxx"
2. Turn text into speech
The response body is the audio itself, and it starts arriving before synthesis finishes. Every sample sets an explicit timeout, because an unbounded request is the most common way a voice integration hangs in production.
curl https://api.quickdial.ai/v1/tts \ -H "Authorization: Bearer $QUICKVOICE_KEY" \ -H "Content-Type: application/json" \ -d '{"text":"Table confirmed.","voice":"azelma"}' \ --max-time 30 \ --output speech.opus
import os, requests r = requests.post( "https://api.quickdial.ai/v1/tts", headers={"Authorization": f"Bearer {os.environ['QUICKVOICE_KEY']}"}, json={"text": "Table confirmed.", "voice": "azelma"}, stream=True, timeout=(3.05, 30), # connect, read - never leave this unset ) r.raise_for_status() with open("speech.opus", "wb") as f: for chunk in r.iter_content(4096): f.write(chunk)
import { writeFile } from "node:fs/promises"; const ac = new AbortController(); const t = setTimeout(() => ac.abort(), 30000); // bound every call try { const res = await fetch("https://api.quickdial.ai/v1/tts", { method: "POST", signal: ac.signal, headers: { Authorization: "Bearer " + process.env.QUICKVOICE_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ text: "Table confirmed.", voice: "azelma" }), }); if (!res.ok) throw new Error("tts " + res.status); await writeFile("speech.opus", Buffer.from(await res.arrayBuffer())); } finally { clearTimeout(t); }
import java.net.URI; import java.net.http.*; import java.nio.file.Path; import java.time.Duration; HttpClient client = HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(5)) .build(); HttpRequest req = HttpRequest.newBuilder(URI.create("https://api.quickdial.ai/v1/tts")) .timeout(Duration.ofSeconds(30)) .header("Authorization", "Bearer " + System.getenv("QUICKVOICE_KEY")) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString( "{\"text\":\"Table confirmed.\",\"voice\":\"azelma\"}")) .build(); HttpResponse<Path> res = client.send( req, HttpResponse.BodyHandlers.ofFile(Path.of("speech.opus"))); if (res.statusCode() != 200) throw new IllegalStateException("tts " + res.statusCode());
package main import ( "bytes" "context" "io" "net/http" "os" "time" ) func main() { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() body := []byte("{\"text\":\"Table confirmed.\",\"voice\":\"azelma\"}") req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.quickdial.ai/v1/tts", bytes.NewReader(body)) req.Header.Set("Authorization", "Bearer "+os.Getenv("QUICKVOICE_KEY")) req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close() f, _ := os.Create("speech.opus") defer f.Close() io.Copy(f, res.Body) }
3. Turn speech into text
Send the audio as multipart form data.
curl https://api.quickdial.ai/v1/stt \ -H "Authorization: Bearer $QUICKVOICE_KEY" \ -F "audio=@meeting.wav" \ --max-time 120
import os, requests with open("meeting.wav", "rb") as f: r = requests.post( "https://api.quickdial.ai/v1/stt", headers={"Authorization": f"Bearer {os.environ['QUICKVOICE_KEY']}"}, files={"audio": f}, timeout=(3.05, 120), ) r.raise_for_status() print(r.json()["text"])
import { openAsBlob } from "node:fs"; const form = new FormData(); form.append("audio", await openAsBlob("meeting.wav"), "meeting.wav"); const res = await fetch("https://api.quickdial.ai/v1/stt", { method: "POST", headers: { Authorization: "Bearer " + process.env.QUICKVOICE_KEY }, body: form, }); if (!res.ok) throw new Error("stt " + res.status); console.log((await res.json()).text);
import java.io.ByteArrayOutputStream; import java.net.URI; import java.net.http.*; import java.nio.file.*; import java.time.Duration; // java.net.http ships no multipart builder, so assemble the body String boundary = "----quickvoice" + System.currentTimeMillis(); byte[] audio = Files.readAllBytes(Path.of("meeting.wav")); ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write(("--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"audio\"; filename=\"meeting.wav\"\r\n" + "Content-Type: audio/wav\r\n\r\n").getBytes()); out.write(audio); out.write(("\r\n--" + boundary + "--\r\n").getBytes()); HttpRequest req = HttpRequest.newBuilder(URI.create("https://api.quickdial.ai/v1/stt")) .timeout(Duration.ofSeconds(120)) .header("Authorization", "Bearer " + System.getenv("QUICKVOICE_KEY")) .header("Content-Type", "multipart/form-data; boundary=" + boundary) .POST(HttpRequest.BodyPublishers.ofByteArray(out.toByteArray())) .build(); HttpResponse<String> res = HttpClient.newHttpClient() .send(req, HttpResponse.BodyHandlers.ofString()); System.out.println(res.body());
package main import ( "bytes" "context" "io" "mime/multipart" "net/http" "os" "time" ) func main() { var buf bytes.Buffer w := multipart.NewWriter(&buf) fw, _ := w.CreateFormFile("audio", "meeting.wav") f, _ := os.Open("meeting.wav") io.Copy(fw, f) f.Close() w.Close() ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) defer cancel() req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.quickdial.ai/v1/stt", &buf) req.Header.Set("Authorization", "Bearer "+os.Getenv("QUICKVOICE_KEY")) req.Header.Set("Content-Type", w.FormDataContentType()) res, _ := http.DefaultClient.Do(req) defer res.Body.Close() out, _ := io.ReadAll(res.Body) os.Stdout.Write(out) }
The transcript comes back as text. Further response fields are proposed but not
frozen; see Speech to Text.
Then what
For a live microphone or an agent turn you want the streaming endpoints. They exist, but their message contract is not published yet, so Streaming sets out exactly what is and is not settled. If you are on LiveKit or Pipecat, use the official plugins rather than writing the transport yourself.
pip install livekit-plugins-quickdial pip install pipecat-quickdial