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

# Text-to-speech

> Synthesize speech with grok-tts, browse the built-in voice catalog, and manage consented custom voice clones — all with the same API key and spend caps as chat.

The AI Reserve gateway exposes xAI's voice synthesis surface under `/v1/audio/speech` with the
same API key, rate limits, wallet, and spend caps as every other route. Two layers of voices are
available:

* **Built-in voices** — 26 xAI voices usable by any caller, no org gate required.
* **Custom voices** — clones of consented speakers, owned and namespaced by your organization
  (org-gated, xAI Enterprise contract required).

<Note>
  **Custom voices are available on request.** The full CRUD surface (`POST /v1/custom-voices` and
  all `/v1/custom-voices/{voiceId}` routes) requires the `customVoicesEnabled` org gate to be on
  for your organization and an xAI Enterprise contract on the platform. Contact your AI Reserve
  representative to enable both. Until enabled, custom-voice endpoints return `403`.

  **Speech synthesis** (`POST /v1/audio/speech`) with built-in voices is available to any org.
  Custom voices additionally require `customVoicesEnabled`.
</Note>

## Built-in voices

`GET /v1/voices` returns the live catalog of voices you can pass as `voice` in any TTS or
realtime session call. No org gate — available to every caller.

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl https://api.aireserve.com/v1/voices \
    -H "Authorization: Bearer $AIRESERVE_API_KEY"
  ```

  ```typescript TypeScript theme={"dark"}
  const response = await fetch("https://api.aireserve.com/v1/voices", {
    headers: { Authorization: `Bearer ${process.env.AIRESERVE_API_KEY}` },
  });
  const { voices } = await response.json();
  ```

  ```python Python theme={"dark"}
  import os, requests

  response = requests.get(
      "https://api.aireserve.com/v1/voices",
      headers={"Authorization": f"Bearer {os.environ['AIRESERVE_API_KEY']}"},
  )
  voices = response.json()["voices"]
  ```
</CodeGroup>

The response is `{ "voices": [ { "voice_id": "ara", "name": "Ara", "language": "en", "gender":
"female" }, … ] }`. The server caches the catalog for 5 minutes and serves a stale list during
brief upstream outages rather than returning an error — the list changes rarely.

Use `GET /v1/voices` for the current catalog. Catalog additions are enabled on synthesis surfaces
after AI Reserve verifies and deploys them; `ara` is the default voice.

## Text-to-speech synthesis

`POST /v1/audio/speech` synthesizes speech from text. The call is synchronous — the full audio
is returned in one response body. The response `Content-Type` is `audio/mpeg`.

### Parameters

| Field             | Required | Description                                                                                      |
| ----------------- | -------- | ------------------------------------------------------------------------------------------------ |
| `model`           | yes      | Must be `"grok-tts"`                                                                             |
| `input`           | yes      | Text to synthesize — max 4,096 Unicode code points                                               |
| `voice`           | yes      | Built-in voice ID or your organization's custom voice ID                                         |
| `language`        | no       | Language hint (e.g. `en`, `en-US`, `es`); when omitted, xAI selects the voice's default language |
| `response_format` | no       | Only `"mp3"` is accepted in this release; defaults to MP3 when omitted                           |

### Quickstart

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl https://api.aireserve.com/v1/audio/speech \
    -H "Authorization: Bearer $AIRESERVE_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"model":"grok-tts","input":"Welcome to AI Reserve.","voice":"ara"}' \
    --output speech.mp3
  ```

  ```typescript TypeScript theme={"dark"}
  const response = await fetch("https://api.aireserve.com/v1/audio/speech", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.AIRESERVE_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "grok-tts",
      input: "Welcome to AI Reserve.",
      voice: "ara",
    }),
  });
  // response body is raw MP3 bytes
  const buffer = Buffer.from(await response.arrayBuffer());
  await Bun.write("speech.mp3", buffer);
  ```

  ```python Python theme={"dark"}
  import os, requests

  response = requests.post(
      "https://api.aireserve.com/v1/audio/speech",
      headers={"Authorization": f"Bearer {os.environ['AIRESERVE_API_KEY']}"},
      json={"model": "grok-tts", "input": "Welcome to AI Reserve.", "voice": "ara"},
  )
  with open("speech.mp3", "wb") as f:
      f.write(response.content)
  ```
</CodeGroup>

### Billing

Billed per **Unicode code point** in `input` at the published `grok-tts` rate (\$15 / 1 M
characters). The character count is known before synthesis begins, so the wallet reservation
equals the settlement amount — no post-call adjustment.

### Limits and notes

* **Input length**: max 4,096 code points per request. For longer text, split at sentence
  boundaries and concatenate the returned audio on your side.
* **Output format**: only MP3 (`audio/mpeg`) in this release. Streaming TTS is deferred — see
  [Deferred features](#deferred-features).
* **Request timeout**: 120 seconds. Long inputs near the 4,096-character ceiling may take
  tens of seconds; set your HTTP client timeout accordingly.
* **Auth requirement**: a client-scoped API key is required (personal keys without org context
  are rejected with `403`).

## Custom voices

Custom voices are speaker clones your organization creates from reference audio. Once created, a
custom voice ID can be used anywhere a built-in voice ID is accepted: `POST /v1/audio/speech`
and `POST /v1/realtime/sessions`.

### Requirements

| Requirement        | Detail                                                                                                                                                                                                |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Org gate**       | `customVoicesEnabled` must be on for your organization — off by default                                                                                                                               |
| **xAI Enterprise** | An xAI Enterprise contract is required on the platform; `POST /custom-voices` returns a shaped `403` until it is active                                                                               |
| **Consent**        | `consent_attestation=true` is required on every create — you attest the speaker consented to the clone                                                                                                |
| **Geography**      | Voice cloning is available in the **United States except Illinois**; do not upload voices from Illinois or create voices whose use would violate biometric, publicity, privacy, or impersonation laws |

### Reference audio recommendations

The quality of the clone depends heavily on the recording:

* **Format**: single-speaker mono WAV at 24 kHz
* **Duration**: 90–120 seconds produces the best results; at least 30 seconds is recommended
* **Environment**: quiet room, no background music, minimal noise
* **Upload limit**: 31 MB (bounds a 120-second uncompressed WAV with multipart overhead)
* **Duration ceiling**: 120 seconds (enforced upstream by xAI)

### Create a custom voice

`POST /v1/custom-voices` is `multipart/form-data`. Required fields are `name`, `language`,
`file`, and `consent_attestation=true`.

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl -X POST https://api.aireserve.com/v1/custom-voices \
    -H "Authorization: Bearer $AIRESERVE_API_KEY" \
    -F "name=Product narrator" \
    -F "language=en-US" \
    -F "tone=warm" \
    -F "use_case=narration" \
    -F "consent_attestation=true" \
    -F "file=@reference.wav;type=audio/wav"
  ```

  ```typescript TypeScript theme={"dark"}
  const form = new FormData();
  form.set("name", "Product narrator");
  form.set("language", "en-US");
  form.set("tone", "warm");
  form.set("use_case", "narration");
  form.set("consent_attestation", "true");
  form.set("file", new Blob([await Bun.file("reference.wav").arrayBuffer()], { type: "audio/wav" }), "reference.wav");

  const response = await fetch("https://api.aireserve.com/v1/custom-voices", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.AIRESERVE_API_KEY}` },
    body: form,
  });
  const voice = await response.json();
  // voice.voice_id is the 8-character ID to use in /audio/speech
  ```

  ```python Python theme={"dark"}
  import os, requests

  with open("reference.wav", "rb") as audio:
      response = requests.post(
          "https://api.aireserve.com/v1/custom-voices",
          headers={"Authorization": f"Bearer {os.environ['AIRESERVE_API_KEY']}"},
          files={"file": ("reference.wav", audio, "audio/wav")},
          data={
              "name": "Product narrator",
              "language": "en-US",
              "tone": "warm",
              "use_case": "narration",
              "consent_attestation": "true",
          },
      )
  voice = response.json()
  # voice["voice_id"] is the 8-character ID to use in /audio/speech
  ```
</CodeGroup>

A successful create returns `201` with the voice object including `voice_id` — an 8-character
lowercase alphanumeric ID assigned by xAI.

**Optional metadata fields**: `description` (free text), `gender` (`male` / `female` /
`neutral`), `accent`, `age` (`young` / `middle-aged` / `old`), `use_case`
(`conversational` / `narration` / `characters` / `educational` / `advertisement` /
`social_media` / `entertainment`), `tone` (`warm` / `casual` / `professional` / `friendly` /
`authoritative` / `expressive` / `calm`).

### Manage custom voices

| Operation        | Endpoint                             | Notes                                                              |
| ---------------- | ------------------------------------ | ------------------------------------------------------------------ |
| List your voices | `GET /v1/custom-voices`              | Returns your org's ownership map, never xAI's team-wide list       |
| Read one voice   | `GET /v1/custom-voices/{voiceId}`    | Refreshes from upstream on every call                              |
| Update metadata  | `PATCH /v1/custom-voices/{voiceId}`  | At least one field required; `name` cannot be `null`               |
| Delete a voice   | `DELETE /v1/custom-voices/{voiceId}` | Removes from xAI and soft-deletes the ownership record; idempotent |

<CodeGroup>
  ```bash cURL — list theme={"dark"}
  curl https://api.aireserve.com/v1/custom-voices \
    -H "Authorization: Bearer $AIRESERVE_API_KEY"
  ```

  ```bash cURL — update theme={"dark"}
  curl -X PATCH https://api.aireserve.com/v1/custom-voices/ab3f9c2e \
    -H "Authorization: Bearer $AIRESERVE_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"name":"Warm narrator v2","tone":"warm"}'
  ```

  ```bash cURL — delete theme={"dark"}
  curl -X DELETE https://api.aireserve.com/v1/custom-voices/ab3f9c2e \
    -H "Authorization: Bearer $AIRESERVE_API_KEY"
  ```
</CodeGroup>

### Tenant isolation

Every custom voice lives on the platform's single xAI team — xAI cannot separate one
organization's voices from another's at the team level. AI Reserve enforces tenant isolation
through an ownership map: every id-addressed operation checks that the voice is owned by the
calling organization. A voice ID owned by a different organization returns `404` — the same
response as a nonexistent ID. Voice existence is never confirmed cross-tenant.

### Capacity

xAI currently limits the shared upstream team to **30 live custom voices**. Delete an unused
voice or contact support when `POST /v1/custom-voices` returns `409`; the API does not expose
other organizations or their usage of the shared capacity.

### Data handling

Reference audio uploaded to create a custom voice is transmitted to xAI, which builds and stores
the resulting voice model. AI Reserve stores only the ownership record and consent attestation —
no reference audio is retained by AI Reserve. See the
[provider data handling page](https://privacy.aireserve.com/data-handling) for details.

## Click-to-copy snippets in the portal

The AI Reserve portal's Voice workspace surfaces the same API operations with a click-to-copy
UI: browse the catalog, preview voices, manage your organization's custom voices, and copy the
exact code snippet for any operation. The portal snippets match the endpoints documented here —
there is no separate portal-only API.

## Deferred features

The following are **not available in this release**:

* **Streaming TTS**: real-time audio streaming from text input. Use
  `POST /v1/realtime/sessions` (speech-to-speech) for live voice sessions.
* **Standalone speech-to-text**: transcription-only calls without synthesis.
  See `POST /v1/audio/transcriptions` for the existing OpenAI-compatible transcription endpoint.
* **SIP / telephony integration**: direct PSTN or SIP dial-in/dial-out.
* **Tool calling over TTS**: function calling is not available on the `grok-tts` surface.
