> ## 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.

# Migrating from Inworld

> Keep your Inworld voice client. Point it at api.aireserve.com/inworld with your AI Reserve API key — TTS, STT and your cloned voices pass through unchanged and land on your AI Reserve ledger.

AI Reserve serves the **Inworld voice API** at `https://api.aireserve.com/inworld`.
An Inworld client whose base URL is repointed here — the `@inworld/tts` Node
package, the `inworld-tts` Python package, the OpenAI SDK pointed at Inworld's
`/v1/audio/speech`, curl — keeps working after **two settings change**: the
base URL and the key. The gateway authenticates your AI Reserve key, swaps it
for AI Reserve's Inworld credential, forwards the request exactly as you sent
it, relays Inworld's answer exactly as Inworld gave it, and books each
successful request to your wallet at Inworld's list price.

Any AI Reserve API key works here; there is no separate credential and no
organization setting to turn on.

| Inworld setting | AI Reserve value |
| - | - |
| Base URL (default `https://api.inworld.ai`) | `https://api.aireserve.com/inworld` — the client appends its own paths, so `/tts/v1/voice` becomes `https://api.aireserve.com/inworld/tts/v1/voice` |
| `INWORLD_API_KEY` from the Inworld portal | Your AI Reserve API key (`aireserve_api_…`). Send it as `Authorization: Basic` or `Authorization: Bearer` — both are accepted on every path |

> [!NOTE]
>
> **This is a passthrough, not a translation.** Request bodies, model ids (`inworld-tts-2`, `inworld-tts-2-flash`,
> `inworld-stt-1`), voice ids, audio configs, response shapes and Inworld's own errors are all Inworld's. The
> AI Reserve-native alternative — `POST /v1/audio/speech` with `grok-tts` or the `eleven-*` models — is documented
> under [Text to speech](https://docs.aireserve.com/capabilities/text-to-speech.md).

## What is served

**Inworld's voice stack.** The gateway forwards any method and any path under
`/inworld/tts/`, `/inworld/stt/`, `/inworld/voices/`, `/inworld/pronunciations/`,
`/inworld/lro/` and `/inworld/v1/audio/` to `api.inworld.ai` exactly as you
sent it. What Inworld does not serve, Inworld answers (`404`, `405`, its own
`4xx`), and that answer is relayed to you unchanged.

| Inworld endpoint | Input → output | Billed as |
| - | - | - |
| `POST /tts/v1/voice` | `text` + `voiceId` + `modelId` → `{ audioContent, usage }` | Characters of `text` at **\$25 per million** (`inworld-tts-2`) or **\$15 per million** (`inworld-tts-2-flash`) |
| `POST /tts/v1/voice:stream` | same → newline-delimited JSON audio chunks | Same as `/tts/v1/voice` |
| `POST /v1/audio/speech` | OpenAI shape: `model` + `voice` + `input` → audio bytes | Characters of `input`, same rates |
| `POST /stt/v1/transcribe` | audio (JSON base64 or multipart) → `{ transcription, usage }` | Audio seconds, rounded up, at **\$0.15 per hour** (`inworld-stt-1`) |
| `GET /tts/v1/voices`, `GET /voices/v1/voices` | — → Inworld's stock catalog **plus your organization's** stored voices | Free |
| `GET /tts/v1/voice:preview` | `voice_id` + `model_id` → preview audio | Free |
| `POST /voices/v1/voices:clone`, `:design`, `{id}:publish`, `PATCH` / `DELETE /voices/v1/voices/{id}` | reference audio or a design prompt → a stored voice owned by your organization | Recorded at \$0 (below) |
| `POST /tts/v1/voice:synthesizeAsync`, `:synthesizeBatch` | same bodies as the sync endpoints (`voiceId`, or `items[].request.voiceId`) → a long-running operation | Recorded at \$0 (below) |
| `/lro/*`, pronunciation dictionaries, professional voice cloning | as Inworld defines it | Recorded at \$0 (below) |

Operations the gateway has no list price for — stored-voice writes, async
and batch synthesis, endpoints Inworld adds later — are still relayed and
still appear in your Event Log, as a `$0` row marked for repair. AI Reserve
reconciles those against Inworld's billing after the fact rather than
refusing the call.

Characters are counted the way Inworld counts them: the text as written,
spaces, punctuation and tags included, with emoji and other characters
outside the Basic Multilingual Plane counting twice. A `modelId` that is not
`inworld-tts-2` or `inworld-tts-2-flash` is forwarded as sent and billed at
the `inworld-tts-2` rate.

Each successful call appears as its own line in **Observe › Event Log** and
**Financials** (model `inworld:inworld-tts-2`, `inworld:inworld-stt-1`, …,
with the metered characters or seconds and the rate in its details). Inworld
does not bill requests that fail, and neither does AI Reserve: a `4xx` or
`5xx` from Inworld is relayed and books nothing.

## What is not served here

Inworld also resells text, image, video and decision models through its
router (`/v1/chat/completions`, `/v1/responses`, `/v1/videos`, `/alpha/decisions`,
…). Those paths answer `403` on this mount: AI Reserve serves the same models
natively at `https://api.aireserve.com/v1` with your AI Reserve key, with the
platform's own pricing and your organization's model governance. The
Inworld Realtime API (`/v1/realtime/*`, WebSocket and WebRTC) is not relayed,
and Inworld's one-time-token, usage and workspace endpoints are AI Reserve's
account data and answer `403`.

## What to change

```diff Shell theme={"dark"}
-export INWORLD_API_KEY="…"
+export INWORLD_API_KEY="aireserve_api_…"                  # your AI Reserve API key
+export INWORLD_BASE_URL="https://api.aireserve.com/inworld"
```

```python Python (inworld-tts) theme={"dark"}
import os
from inworld_tts import InworldTTS

client = InworldTTS(
    api_key=os.environ["INWORLD_API_KEY"],         # aireserve_api_…
    base_url="https://api.aireserve.com/inworld",
)

audio = client.tts.synthesize(text="Hello from Inworld, through AI Reserve.", voice_id="Ashley", model_id="inworld-tts-2")
with open("hello.mp3", "wb") as f:
    f.write(audio.audio_bytes)
```

```ts TypeScript (@inworld/tts) theme={"dark"}
import { InworldTTSClient } from "@inworld/tts";

const inworld = new InworldTTSClient({
  apiKey: process.env.INWORLD_API_KEY,             // aireserve_api_…
  baseUrl: "https://api.aireserve.com/inworld",
});

const audio = await inworld.tts.synthesize({ text: "Hello from Inworld, through AI Reserve.", voiceId: "Ashley", modelId: "inworld-tts-2" });
const voices = await inworld.voices.list();        // stock voices + your organization's clones
```

```ts OpenAI SDK (drop-in TTS) theme={"dark"}
import OpenAI from "openai";

const openai = new OpenAI({ apiKey: process.env.INWORLD_API_KEY, baseURL: "https://api.aireserve.com/inworld/v1" });
const speech = await openai.audio.speech.create({ model: "inworld-tts-2-flash", voice: "Ashley", input: "Hello from AI Reserve." });
```

```bash curl theme={"dark"}
# Text to speech, native shape
curl -X POST https://api.aireserve.com/inworld/tts/v1/voice \
  -H "Authorization: Basic $INWORLD_API_KEY" -H "Content-Type: application/json" \
  -d '{"text":"Hello from Inworld, through AI Reserve.","voiceId":"Ashley","modelId":"inworld-tts-2"}'

# Speech to text
curl -X POST https://api.aireserve.com/inworld/stt/v1/transcribe \
  -H "Authorization: Basic $INWORLD_API_KEY" \
  -F 'transcribeConfig={"modelId":"inworld-stt-1"};type=application/json' -F "file=@meeting.wav"

# Voice list
curl "https://api.aireserve.com/inworld/voices/v1/voices?pageSize=20" -H "Authorization: Basic $INWORLD_API_KEY"
```

## Voice cloning and designed voices

Inworld's instant voice cloning (`POST /voices/v1/voices:clone` with
reference audio, returning a reusable `voiceId`), voice design
(`POST /voices/v1/voices:design` for previews, then
`POST /voices/v1/voices/{id}:publish` to keep one) and voice copy tokens
(`:issueCopyToken` / `:redeemCopyToken`) work through AI Reserve exactly as
on Inworld. Professional voice cloning (`/voices/v1/pvcVoices`) is relayed
too, but a professionally cloned voice is registered to your organization
by AI Reserve support once Inworld delivers it — until then it is not
reachable through your key. Contact support when you start a professional
clone.

**Stored voices are scoped to your organization.** Every AI Reserve
organization reaches Inworld through one AI Reserve workspace, so the
gateway, not Inworld, keeps your voices yours: a voice cloned, published or
redeemed with your key belongs to your organization; the voice lists show
Inworld's stock catalog plus only your organization's voices; and every
place a request names a stored voice — `GET`, `HEAD`, `PATCH`,
`DELETE /voices/v1/voices/{id}`, `:issueCopyToken`, the preview's
`voice_id`, `voiceId` on sync, streaming and async synthesis, `voice` on
`/v1/audio/speech`, and every item of a batch — accepts your own voices and
the stock catalog. Another organization's voice id answers Inworld's own
`404` (`voice not found`) — it does not exist as far as your key is
concerned.

Each synthesis endpoint reads the voice from one field (`voiceId` on the
native endpoints, `voice` on `/v1/audio/speech`, `items[].request.voiceId`
on a batch). A body that also carries the other field, or that is not a
JSON object the gateway can read (send `Content-Type: application/json`),
is refused with `400` before it is forwarded, so the voice the gateway
checked is always the voice Inworld speaks with.

## What AI Reserve enforces

Every refusal is a JSON `{ "code": <grpc code>, "message": "…", "details": [] }`
body, the shape Inworld's own APIs use, and names its cause in `message`.

* **Your organization's policies.** The key is checked exactly as on every AI
  Reserve endpoint: paused keys (`401`), invalid keys (`401`, `Invalid API
  key.`), per-key IP allowlists and organization network policies (`403`),
  rate limits (`429` with `Retry-After`), and — on every `POST` — your
  organization's spend caps and wallet (`402`). An Inworld request is a
  wallet request: an exhausted budget refuses it before Inworld is called.
* **Your voices stay yours.** A stored voice that belongs to another AI
  Reserve organization — named in the path of `/voices/v1/voices/{id}` or as
  the `voiceId` / `voice` of a synthesis — answers `404` `voice not found`,
  exactly as Inworld answers for a voice that does not exist. A voice in AI
  Reserve's workspace that no organization has claimed is refused the same
  way. If the ownership check itself cannot run, the call is refused with
  `503` rather than forwarded unverified. A voice whose ownership could not
  be recorded at creation is removed from Inworld again and the create
  answers `503`; retry it.
* **Metering, never filtering.** The gateway reads the `text` / `input`,
  `voiceId` / `voice` and `modelId` / `model` of a synthesis body and the
  `usage` of a transcription answer to price and scope the call; it never
  changes, drops or validates what you sent. A body it cannot read is still
  forwarded and, if Inworld serves it, recorded at `$0` marked for repair.
  The one answer the gateway edits is a voice list, where other
  organizations' voices are removed.
* **Headers.** Your `Authorization` and `x-api-key` are replaced by AI
  Reserve's Inworld credential; `Accept-Encoding` is pinned to `identity` so
  Inworld's bytes are the bytes relayed. Content types and your client's
  user agent go through.
* **A request log.** Each call is recorded with method, path, upstream
  status, byte counts, time-to-headers, duration, outcome, your key id, and
  the metered amount. Refusals are recorded with their outcome before any
  upstream request. Neither includes bodies, audio, or credentials.
* **Transport.** Inworld unreachable → `503`; no response headers within the
  time-to-headers bound → `504`; a stream that has started is never cut by
  that bound.

## Limits

* **Rate limits are Inworld's account tiers, shared.** Inworld limits
  requests and concurrency per account, not per key; AI Reserve's account
  tier is shared across every organization using this endpoint, and a `429`
  from Inworld is relayed as Inworld sends it (a plain-text `Rate limit
  exceeded`, no `Retry-After`). Back off exponentially.
* **One Inworld workspace, Inworld's data handling.** Text, audio uploads and
  reference audio reach Inworld under AI Reserve's workspace and Inworld's
  terms. See the [provider data-handling page](https://privacy.aireserve.com/data-handling).
* **HTTP only.** The gateway relays HTTP requests. Inworld's WebSocket
  endpoints (`/v1/tts/synthesize:websocket`, `/stt/v1/transcribe:streamBidirectional`)
  and the Realtime API are not upgraded through the gateway;
  `POST /tts/v1/voice:stream` streams its chunks as they are synthesized.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.