> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orq.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a voice loop with transcription and text-to-speech

> Compose transcription and text-to-speech around a model call to build a voice-in, voice-out loop.

<Info>
  TL;DR

  * **Orq.ai** has no single voice endpoint: voice in and voice out are two dedicated calls composed around a normal chat completion, not a chat mode.
  * This walkthrough builds that loop with four calls: get a clip, transcribe it, get an answer, speak the answer back.
</Info>

## What you'll build

A four-call voice loop: a spoken question goes in, gets transcribed to text, gets answered by a model, and the answer comes back out as audio. Every call is a plain request against the **AI Gateway**, shown in cURL, TypeScript, and Python.

## What you'll learn

* How to call the dedicated transcription and text-to-speech endpoints
* Why there is no single "voice mode" call, and how to compose the two endpoints around a normal chat completion instead

## Prerequisites

* An [Orq.ai](https://my.orq.ai) workspace and API key, available as `$ORQ_API_KEY`
* `curl`, or the `openai` package for TypeScript (`npm install openai`) or Python (`pip install openai`)

No **Orq.ai** SDK or UI step is required. The **AI Gateway** is OpenAI-compatible, so the standard `openai` client works against it with just a different `baseURL`.

The cURL blocks below are independent, standalone commands. Copy each one's output into the next by hand. The TypeScript and Python blocks build one continuous script instead. Add each block to the same file in order.

## Step 1: Get a clip to work with

Generate a short spoken clip with the same text-to-speech endpoint used again in Step 4, so nothing outside this page is needed to follow along. Skip this step if starting from an existing audio file instead.

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X POST https://my.orq.ai/v3/router/audio/speech \
    -H "Authorization: Bearer $ORQ_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "openai/tts-1",
      "voice": "alloy",
      "input": "What is the capital of France, and what is one place I should visit there?",
      "response_format": "mp3"
    }' \
    --output question.mp3
  ```

  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import OpenAI from "openai";
  import fs from "fs";

  const client = new OpenAI({
    apiKey: process.env.ORQ_API_KEY,
    baseURL: "https://my.orq.ai/v3/router",
  });

  const speech = await client.audio.speech.create({
    model: "openai/tts-1",
    voice: "alloy",
    input: "What is the capital of France, and what is one place I should visit there?",
    response_format: "mp3",
  });

  fs.writeFileSync("question.mp3", Buffer.from(await speech.arrayBuffer()));
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import os
  from openai import OpenAI

  client = OpenAI(
      api_key=os.environ["ORQ_API_KEY"],
      base_url="https://my.orq.ai/v3/router",
  )

  with client.audio.speech.with_streaming_response.create(
      model="openai/tts-1",
      voice="alloy",
      input="What is the capital of France, and what is one place I should visit there?",
      response_format="mp3",
  ) as speech:
      speech.stream_to_file("question.mp3")
  ```
</CodeGroup>

`question.mp3` now holds the spoken question.

## Step 2: Transcribe it to text

Send the clip to the transcription endpoint.

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X POST https://my.orq.ai/v3/router/audio/transcriptions \
    -H "Authorization: Bearer $ORQ_API_KEY" \
    -F "model=openai/gpt-4o-transcribe" \
    -F "file=@question.mp3" \
    -F "response_format=json"
  ```

  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const transcription = await client.audio.transcriptions.create({
    model: "openai/gpt-4o-transcribe",
    file: fs.createReadStream("question.mp3"),
    response_format: "json",
  });

  console.log(transcription.text);
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  with open("question.mp3", "rb") as audio_file:
      transcription = client.audio.transcriptions.create(
          model="openai/gpt-4o-transcribe",
          file=audio_file,
          response_format="json",
      )

  print(transcription.text)
  ```
</CodeGroup>

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{"text":"What is the capital of France and what is one place I should visit there?"}
```

## Step 3: Get a response

Pass the transcript straight into a normal chat completion. This is the same **AI Gateway** call used anywhere else. Nothing audio-specific about it.

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X POST https://my.orq.ai/v3/router/chat/completions \
    -H "Authorization: Bearer $ORQ_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "openai/gpt-4o-mini",
      "messages": [
        {"role": "user", "content": "What is the capital of France and what is one place I should visit there?"}
      ]
    }'
  ```

  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const completion = await client.chat.completions.create({
    model: "openai/gpt-4o-mini",
    messages: [{ role: "user", content: transcription.text }],
  });

  const answer = completion.choices[0].message.content ?? "";
  console.log(answer);
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  completion = client.chat.completions.create(
      model="openai/gpt-4o-mini",
      messages=[{"role": "user", "content": transcription.text}],
  )

  answer = completion.choices[0].message.content or ""
  print(answer)
  ```
</CodeGroup>

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "object": "chat.completion",
  "model": "gpt-4o-mini",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "The capital of France is Paris. One iconic place you should visit there is the Eiffel Tower. It's a symbol of Paris and offers stunning views of the city from its observation decks.",
        "refusal": null
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 23,
    "completion_tokens": 36,
    "total_tokens": 59
  }
}
```

Chat completions are not deterministic. Wording varies slightly between runs. The content above is one real captured response, not a fixed value to match exactly. Internal bookkeeping fields (`id`, `created`, `system_fingerprint`, token detail breakdowns) are omitted here since they don't affect how to use the response.

## Step 4: Speak the response back

Send the model's answer to the text-to-speech endpoint, this time with an **ElevenLabs** voice.

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl -X POST https://my.orq.ai/v3/router/audio/speech \
    -H "Authorization: Bearer $ORQ_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "elevenlabs/eleven_multilingual_v2",
      "voice": "aria",
      "input": "The capital of France is Paris. One iconic place you should visit there is the Eiffel Tower. It'\''s a symbol of Paris and offers stunning views of the city from its observation decks.",
      "response_format": "mp3"
    }' \
    --output answer.mp3
  ```

  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  const spokenAnswer = await client.audio.speech.create({
    model: "elevenlabs/eleven_multilingual_v2",
    voice: "aria",
    input: answer,
    response_format: "mp3",
  });

  fs.writeFileSync("answer.mp3", Buffer.from(await spokenAnswer.arrayBuffer()));
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  with client.audio.speech.with_streaming_response.create(
      model="elevenlabs/eleven_multilingual_v2",
      voice="aria",
      input=answer,
      response_format="mp3",
  ) as spoken_answer:
      spoken_answer.stream_to_file("answer.mp3")
  ```
</CodeGroup>

`answer.mp3` is a spoken version of the model's answer. The loop is complete: spoken question in, spoken answer out, with no single endpoint doing both.

<Tip>
  For the full model, voice, and parameter reference across every supported provider, see [Audio](/docs/ai-gateway/features/multimodal#audio) on the Multimodal page. This walkthrough only covers what is needed to compose the loop above.
</Tip>

This pattern generalizes past a single question and answer. Any voice interface on **Orq.ai** composes the same way: transcribe the input, run it through a model, speak the response back. Swap Step 3 for a different model, a system prompt, or a full agent. The surrounding transcription and text-to-speech calls stay the same.

## Next steps

* [Traces](/docs/ai-studio/observability/traces), inspect each call in this loop after it runs
* [Build Agents](/docs/ai-studio/ai-engineering/build-agents), replace the bare chat completion in Step 3 with a full Agent
* [Receipt Extraction](/docs/ai-studio/cookbooks/data-extraction/receipt-extraction), the same non-text-input pattern applied to images instead of audio
