# Payment & credits Source: https://docs.boson.ai/account-billing/payment-and-credits How Boson AI billing works: prepaid credits, payment methods, auto-reload, and invoices. Boson AI billing is **prepaid**: you add credits to your balance, and API usage draws the balance down at the standard [pricing](/pricing) rates. When the balance reaches zero, requests are refused until you add more — see [Common error types](/api-reference/errors). New accounts start with [free trial credit](/free-trial-credit); everything below applies once you're ready to go beyond it. ## Add credits Buy credits from [billing overview](https://www.boson.ai/workspace/billing/overview). Each purchase is between \$5 and \$100 — pick a preset or enter a custom amount. Taxes are estimated at checkout and may differ from the final amount. ## Payment methods Add and manage cards on the [billing payment page](https://www.boson.ai/workspace/billing/payment). Card details are handled by Stripe; Boson AI does not store your card number. ## Auto-reload Turn on auto-reload from [billing overview](https://www.boson.ai/workspace/billing/overview) so service never stops because a balance quietly ran out mid-project. You configure two values: * **When balance below** — the threshold that triggers a reload. The minimum is \$5. * **Bring balance up to** — the balance target a reload restores. The maximum is \$100, and the target must sit \$30 or more above the threshold. Whenever your balance falls below the threshold, it is automatically recharged up to the target using your saved payment method, and you're notified by email whether the reload succeeded or failed. The workspace also warns you when your balance runs low either way. ## Invoices and transactions The [billing payment page](https://www.boson.ai/workspace/billing/payment) also holds your **transactions** and **invoices** history. ## When payment lapses If your balance is empty (or a [spend cap](/account-billing/usage-and-limits) is hit), API requests fail with a `429` `insufficient_quota` error, and Realtime sessions receive an `error` event and close with code `4429`. Add credits to resume service. # Usage & limits Source: https://docs.boson.ai/account-billing/usage-and-limits Track spend, set a monthly cap, and understand what happens when a limit is reached. ## Track your usage The [usage page](https://www.boson.ai/workspace/billing/usage) shows your spend over time and a summary by model. Your current balance and recent activity are on [billing overview](https://www.boson.ai/workspace/billing/overview). ## Spend limits From the usage page you can set: * a **monthly cap** — a hard ceiling on monthly spend, and * a **warning threshold** — you're alerted when spend crosses it, before the cap is reached. While a monthly cap is reached, requests are refused even if your balance is positive. The cap resets at the beginning of each month (UTC); to resume sooner, raise or remove the cap. ## What a refusal looks like Whether the cause is an empty balance or a reached cap, the API responds the same way: * REST requests fail with **`429`** and an error body with `"type": "insufficient_quota"`. * Realtime sessions receive an `error` event (`type: "insufficient_quota"`) with the upstream message, then close with WebSocket code `4429`. See [Common error types](/api-reference/errors) for the full error reference, and [Payment & credits](/account-billing/payment-and-credits) for how to add credits. ## Concurrency Realtime connections are also subject to a concurrency limit: exceeding it closes the WebSocket with code `1013` (retry later). See the [Realtime API overview](/api-reference/realtime/overview) for the close-code table. # Create a speech Source: https://docs.boson.ai/api-reference/audio/create-a-speech /openapi.json post /v1/audio/speech Generate speech audio from text. Returns an audio file, or a stream of raw PCM chunks when `stream` is `true`. The body may be JSON or `multipart/form-data` — the latter lets you upload `ref_audio` as a raw file instead of base64-encoding it. # Create a voice Source: https://docs.boson.ai/api-reference/audio/create-a-voice /openapi.json post /v1/audio/voices Register a reusable reference voice for cloning. Identical audio re-registered under the same API key returns the existing voice. Pass the returned `voice` ID to the `voice` field of `POST /v1/audio/speech` instead of sending `ref_audio` on every request. # Get a voice Source: https://docs.boson.ai/api-reference/audio/get-a-voice /openapi.json get /v1/audio/voices/{voice} Fetch a single reference voice by ID. # List voices Source: https://docs.boson.ai/api-reference/audio/list-voices /openapi.json get /v1/audio/voices List the reference voices registered under your API key. # Common error types Source: https://docs.boson.ai/api-reference/errors HTTP statuses, the error body shape, and every error code with its fix. ## Error format REST API errors return a JSON body with a single `error` object: ```json theme={null} { "error": { "message": "Your API key is not linked to an active billing account. Please go to Boson.ai to set up billing and purchase credits.", "type": "insufficient_quota", "param": null, "code": "no_billing_account" } } ``` `message` (string, human-readable), `type` (string, error category), `param` (string or null, the offending parameter if any), `code` (string or null, machine-readable cause). Handle errors by `type` (the stable category) and use `code` to distinguish causes within it. ## Error types | Status | `type` | Cause | Fix | | ------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | `401` | `authentication_error` | Problem with your API key — missing, malformed, or revoked (`code: "invalid_api_key"`). | Check the `Authorization: Bearer` header and the key in your [workspace](https://www.boson.ai/workspace/api-key). See [Authentication](/authentication). | | `400` / `403` | `invalid_request_error` | Malformed request, or the key lacks access to the requested model. | Fix the request per the `message`; for model access, contact support. | | `429` | `rate_limit_error` | Too many requests — **transient**. | Retry with backoff. | | `429` | `insufficient_quota` | Billing refusal — **not transient**; retrying cannot succeed until the account changes. See the code table below. | Resolve in [workspace billing](https://www.boson.ai/workspace/billing/overview), not in code. | | `5xx` | `api_error` | Something failed on the server side. | Retry later; report if persistent. | Both `rate_limit_error` and `insufficient_quota` arrive as `429` — check `error.type` before retrying. A `rate_limit_error` recovers on its own; an `insufficient_quota` never does. ### `insufficient_quota` codes | `code` | Meaning | Fix | | --------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | | `no_billing_account` | The key isn't linked to an active billing account — typically a new account that hasn't claimed credit. | [Claim your free trial credit](/free-trial-credit). | | `insufficient_quota` | Credits are exhausted. | [Add credits](/account-billing/payment-and-credits). | | `monthly_cap_reached` | Your own [monthly spend cap](/account-billing/usage-and-limits) is reached. | Raise or remove the cap; it also resets at the start of each month (UTC). | | `contract_ended` | The account's contract has ended. | Contact your Boson AI representative. | ## Realtime errors The Realtime WebSocket signals problems with an [`error` server event](/api-reference/realtime/server-events#error) carrying the same `type` / `code` / `message` / `param` fields. Billing refusals use `type: "insufficient_quota"` and are followed by WebSocket close code `4429`; invalid or expired keys close with code `3000`. See the [close-code table](/api-reference/realtime/overview) for all codes. ## Product-specific request errors * [Avatar input limits and request errors](/models/higgs-avatar/input-options#input-limits) * [Realtime session limits](/models/higgs-realtime/guides/connections-and-sessions) # Client events Source: https://docs.boson.ai/api-reference/realtime/client-events Configure a Realtime session, provide input, manage conversation items, and control responses.
Events are sent as JSON text frames over the WebSocket. Every client event has a required `type` and may carry an optional `event_id` (string; an identifier is generated when omitted). Unknown or malformed events produce an `error` event. ## `session.update` Create or update the session configuration. The first `session.update` starts the session and is acknowledged with `session.created`; every later one is acknowledged with `session.updated`. An invalid configuration produces an `error` and the session is closed. Always `session.update`. Client-chosen event identifier. The [session configuration object](#session-configuration-object). The model comes from `session.model` or, when it is omitted, from the connection URL's `?model=` query parameter; the first `session.update` fails if neither is set. ## `input_audio_buffer.append` Append an audio chunk to the input buffer. The audio must be encoded in the configured `audio.input.format`. With server VAD, buffered audio is consumed automatically as speech is detected; with `turn_detection: null`, audio accumulates until `input_audio_buffer.commit`. The server does not acknowledge each append. Always `input_audio_buffer.append`. Client-chosen event identifier. Base64-encoded audio in the session's input format. Maximum 1,048,576 base64 bytes per event (≈15 s of 24 kHz PCM16); larger chunks produce an `error`. ## `input_audio_buffer.commit` Commit the buffered audio as a user turn (manual turn detection, i.e. `turn_detection: null`). The server emits `input_audio_buffer.committed`, transcribes the audio, and adds the user item to the conversation (`conversation.item.added`). Committing does not generate a response — send `response.create`. Rejected with an `error` (`type: "voice_output_task_ongoing"`) while a response is being generated. Always `input_audio_buffer.commit`. Client-chosen event identifier. ## `input_audio_buffer.clear` Discard all uncommitted audio in the input buffer. Acknowledged with `input_audio_buffer.cleared`. Always `input_audio_buffer.clear`. Client-chosen event identifier. ## `conversation.item.create` Add an item to the conversation — a text message, or a `function_call_output` returning a tool result. Acknowledged with `conversation.item.added`. Creating an item never triggers a response by itself; send `response.create` when you want one. Always `conversation.item.create`. Client-chosen event identifier. Id of the item to insert after. Omitted or `null` appends at the end of the conversation. An unknown id produces an `error` (`type: "invalid_previous_item_id"`). The item to add. See supported item types below. Supported `item` types: | `item.type` | Fields | Description | | ---------------------- | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `message` | `role` (`"user"` \| `"assistant"`), `content` (array), `id` (string, optional) | A conversation message. `content` must be text — `[{"type": "input_text", "text": "..."}]` for `user` messages, `[{"type": "text", "text": "..."}]` for `assistant` messages; items with non-text content are skipped. | | `function_call` | `call_id`, `name`, `arguments` (strings), `id` (string, optional) | A past tool call — useful when rebuilding conversation history. | | `function_call_output` | `call_id` (string), `output` (string), `id` (string, optional) | The result of a tool call your client executed. `call_id` must echo the `call_id` from the `function_call` item. | A client-supplied `item.id` is preserved so the item can be addressed later (retrieve / truncate / delete); an id that already exists produces an `error` (`type: "conversation_item_duplicate_id"`). When omitted, the server generates one. ## `conversation.item.retrieve` Fetch the server's full copy of a conversation item by id — typically to inspect a user audio item (its audio content and transcript) as captured server-side, or to fetch an older item condensed away by `conversation.context.summarized`. Answered with `conversation.item.retrieved`, or an `error` (`type: "conversation_item_not_found"`). Always `conversation.item.retrieve`. Client-chosen event identifier. Id of the item to fetch. ## `conversation.item.truncate` Truncate a completed assistant item to what the user actually heard — use it when your client stopped playback early, so the stored transcript matches the audio played. Acknowledged with `conversation.item.truncated`. In text-only sessions (`output_modalities: ["text"]`) this is a no-op acknowledged with `audio_end_ms: 0`. Always `conversation.item.truncate`. Client-chosen event identifier. Id of the assistant item to truncate. Index of the content part to truncate. Playback position, in milliseconds from the start of the item's audio, at which to cut. ## `conversation.item.delete` Remove an item from the conversation. Acknowledged with `conversation.item.deleted`. Always `conversation.item.delete`. Client-chosen event identifier. Id of the item to remove. ## `response.create` Request a model response. Without a `response` body, the response is generated from the current conversation and session configuration. With server VAD, any in-flight response is interrupted first; with `turn_detection: null`, the request is rejected with an `error` (`type: "voice_output_task_ongoing"`) while a response is active. Always `response.create`. Client-chosen event identifier. Per-response overrides. System prompt for this response. Message items to respond to: `[{"type": "message", "role": "user", "content": [{"type": "input_text", "text": "..."}]}]`. The messages are also appended to the conversation. Sampling temperature for this response only. Output token cap for this response only. Attached to the `response.created` event's response object (`response.done` does not echo it). ## `response.cancel` Cancel the in-flight response. The cancelled response finishes with `response.done` (`status: "cancelled"`). Always `response.cancel`. Client-chosen event identifier. When set, must match the active response's id, else an `error` (`code: "response_id_mismatch"`). When no response is active, an `error` (`code: "response_not_active"`). ## Session configuration object Passed as `session` in `session.update`: ```json theme={null} { "type": "realtime", "model": "higgs-realtime", "instructions": "You are a helpful AI assistant", "output_modalities": ["audio"], "audio": { "input": { "format": { "type": "audio/pcm", "rate": 24000 }, "noise_reduction": { "type": "near_field" }, "transcription": { "model": "higgs-stt-3.1", "language": null }, "turn_detection": { "type": "server_vad", "threshold": 0.55, "prefix_padding_ms": 300, "silence_duration_ms": 500, "min_speech_duration": 0.125 } }, "output": { "format": { "type": "audio/pcm", "rate": 24000 }, "voice": "default" } }, "tools": [], "tool_choice": "auto", "temperature": 0.3, "max_output_tokens": "inf", "truncation": "auto" } ``` Constraints: * `model` is optional when the connection URL carries `?model=` (an explicit `session.model` overrides the URL); the first `session.update` fails if neither is set. Use `higgs-realtime`. * `output_modalities` must be exactly `["audio"]` or `["text"]`. * `max_output_tokens` integers are clamped to `4096`; `"inf"` is unbounded. * `audio.*.format.type` is one of `audio/pcm` (`rate`: 8000 | 16000 | 24000 | 48000), `audio/pcmu`, `audio/opus` (`frame_size_ms`: 2.5 | 5 | 10 | 20 | 40 | 60). * `turn_detection.type` is `server_vad` or `semantic_vad`. * A non-`"default"` `voice` is validated against the voices API at update time; an unknown voice fails the `session.update`. * `transcription.model` enables input transcription: set it to `higgs-stt-3.1` to receive `conversation.item.input_audio_transcription.completed` events; when unset, no transcription events are emitted. `language` is an optional ISO-639-1 hint. * `tools[]` entries: `{ "type": "function", "name", "description", "parameters" }`. ## Related The events the server sends back in response to these. How to connect, authenticate, and choose session settings. When to commit audio yourself versus letting server VAD do it. Declaring tools and returning results with `function_call_output`.
*** # Create a client secret Source: https://docs.boson.ai/api-reference/realtime/client-secrets openapi.json POST /v1/realtime/client_secrets Mint a short-lived credential for browser and client-side Higgs Realtime connections. *** # Overview Source: https://docs.boson.ai/api-reference/realtime/overview Connect to Higgs Realtime and navigate the REST and WebSocket protocol reference.
REST and WebSocket reference for the realtime voice API. All routes are prefixed with `/v1/realtime`. For a guided walkthrough, see [Speech to Speech](/models/higgs-realtime/overview). *** ## WebSocket connection ```text WebSocket endpoint theme={null} wss://api.boson.ai/v1/realtime ``` Establishes a realtime voice session. The connection upgrades from HTTP GET (status 101) and then exchanges JSON events for session configuration, audio streaming, and responses. **Query parameters** | Parameter | Purpose | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `model` | Optional; the session model (`higgs-realtime`). Used when `session.update` omits `session.model`; an explicit `session.model` overrides it. | **Subprotocols** | Value | Purpose | | ------------------------- | --------------------------------------------------------------- | | `realtime` | Optional; echoed back as the negotiated subprotocol if offered. | | `bai-client-secret.` | Client-side auth with an ephemeral key. | **Authentication** Either an `Authorization: Bearer ` header (server-side), or an ephemeral key (`bai-eph-…`) passed via subprotocol (client-side). Ephemeral keys are minted with [`POST /v1/realtime/client_secrets`](/api-reference/realtime/client-secrets). **WebSocket close codes** | Code | Meaning | | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `1000` | Normal closure — including session-limit closes (idle timeout, max session duration), which are preceded by their server event and carry the reason in the close frame. | | `1013` | Max concurrency exceeded — retry later. | | `3000` | Invalid API key or invalid/expired ephemeral key. | | `4429` | Billing entitlement refused (quota exhausted, spending cap, contract ended). Always preceded by an `error` event carrying the upstream message. Check your balance and add credits in [workspace billing](https://www.boson.ai/workspace/billing/overview). | *** ## WebRTC WebRTC transport support is in progress. WebSocket is the supported transport today.
# Server events Source: https://docs.boson.ai/api-reference/realtime/server-events Handle session state, input audio, streamed responses, tool calls, errors, and lifecycle events.
Events sent by the server as JSON text frames. Every server event carries a server-generated `event_id` (string) and a `type`. ## Session and conversation ### `session.created` Sent in response to the first `session.update`. Contains the session id — read yours from `session.id`. The acknowledged session configuration, plus `id` (string) and `object: "realtime.session"`. ### `session.updated` Acknowledges each `session.update` after the first. Same shape as in `session.created`. ### `conversation.item.added` An item entered the conversation — user turns, assistant replies, tool calls and results, and client-created items alike. The [conversation item](#conversation-items). Id of the item immediately before this one; `null` for the first item. ### `conversation.item.retrieved` Answer to `conversation.item.retrieve`. The requested [conversation item](#conversation-items). ### `conversation.item.truncated` Acknowledges `conversation.item.truncate`, echoing its fields. Id of the truncated item. Index of the truncated content part. Cut position in milliseconds (`0` for the text-only no-op). ### `conversation.item.deleted` Acknowledges `conversation.item.delete`. Id of the removed item. ### `conversation.context.summarized` With `truncation: "auto"`, older conversation items were condensed into a summary to stay within the model's context window. The summarized items remain retrievable via `conversation.item.retrieve`. Extension; not part of the OpenAI Realtime API — clients built on OpenAI SDK event types should tolerate this unknown event type. The summary text. Ids of the items condensed into the summary. Id of the new summary item. Context token count before summarization. Context token count after summarization. ## Input audio ### `input_audio_buffer.speech_started` Server VAD detected the start of user speech. If the assistant is speaking, this is the barge-in signal — stop local playback. Id of the user item this speech will be added to. Position in the input audio stream, in milliseconds, where speech begins (includes `prefix_padding_ms`). ### `input_audio_buffer.speech_stopped` Server VAD detected the end of user speech; a response follows automatically. Id of the user item. Position in the input audio stream, in milliseconds, where speech ends. ### `input_audio_buffer.committed` The input buffer was committed as a user item — after a client `input_audio_buffer.commit`. Id of the user item the audio was committed to. Id of the item before it, or `null`. ### `input_audio_buffer.cleared` Acknowledges `input_audio_buffer.clear`. No fields beyond `event_id` and `type`. ### `conversation.item.input_audio_transcription.completed` Final transcript of a user audio turn. Only emitted when `audio.input.transcription.model` is configured (`higgs-stt-3.1`); when unset, no transcription events are emitted. Id of the user item the transcript belongs to. Index of the audio content part that was transcribed. The transcript text. ## Response lifecycle ### `response.created` Response generation started. The [response object](#response-object) with `status: "in_progress"`. Its `metadata` echoes the `metadata` from your `response.create`, letting you correlate this response with the request that triggered it (`null` when none was sent, e.g. VAD-triggered responses). ### `response.output_item.added` An output item (assistant message or tool call) was added to the response. Id of the response. Index of the item in the response output. The [conversation item](#conversation-items). ### `response.output_item.done` An output item finished streaming. Fields as in `response.output_item.added`, with the completed item. ### `response.content_part.added` A content part started streaming within an output item. Id of the response. Id of the output item. Index of the item in the response output. Index of the part within the item's content. Content part: `type` (`"text"` | `"audio"`), `audio` (string, base64, or null), `transcript` (string or null). ### `response.content_part.done` A content part finished streaming. Fields as in `response.content_part.added`, with the completed part. ### `response.output_audio.delta` A chunk of output audio, encoded in the configured `audio.output.format`. Id of the response. Id of the output item. Index of the item in the response output. Index of the audio content part. Base64-encoded audio chunk. ### `response.output_audio.done` The audio stream for a content part completed. Same fields as `response.output_audio.delta`, without `delta`. ### `response.output_audio_transcript.delta` Streaming transcript of the audio the assistant is speaking. Id of the response. Id of the output item. Index of the item in the response output. Index of the content part. Transcript text fragment. ### `response.output_audio_transcript.length` A transcript fragment annotated with the duration of its corresponding audio — useful for aligning captions with playback. Extension; not part of the OpenAI schema. Id of the response. Id of the output item. Index of the item in the response output. Index of the content part. Transcript text fragment. Duration in milliseconds of the audio corresponding to `delta`. ### `response.output_audio_transcript.done` The spoken transcript for a content part is complete. Id of the response. Id of the output item. Index of the item in the response output. Index of the content part. The full transcript of the spoken audio. ### `response.output_text.delta` A fragment of streamed text output. Emitted instead of audio events when `output_modalities` is `["text"]`. Id of the response. Id of the output item. Index of the item in the response output. Index of the content part. Text fragment. ### `response.output_text.done` The text output for a content part is complete. Note this event carries no `item_id` — correlate via `response.output_item.done`. Id of the response. Index of the item in the response output. Index of the content part. The full text output. ### `response.function_call_arguments.delta` Streaming fragment of a tool call's arguments. Id of the response. Id of the `function_call` item. Index of the item in the response output. Id of the tool call; echo it in `function_call_output`. JSON arguments fragment. ### `response.function_call_arguments.done` A tool call's arguments are complete. Execute the function and return the result via `conversation.item.create` (`function_call_output`), then send `response.create`. Id of the response. Id of the `function_call` item. Index of the item in the response output. Name of the function to call. Id of the tool call; echo it in `function_call_output`. Complete JSON-encoded arguments. ### `response.done` The response finished. Emitted exactly once per `response_id`. A response that ends in a tool call carries the completed `function_call` item in `response.output`. The [response object](#response-object). `status` is `completed` or `cancelled` (interrupted / `response.cancel`). The schema also defines `incomplete` and `failed`, but the server does not currently emit them. ## Session control and status ### `error` Something went wrong. Billing refusals use `error.type: "insufficient_quota"` and precede WebSocket close code `4429`; check your balance and add credits in [workspace billing](https://www.boson.ai/workspace/billing/overview). `type` (string, error category), `code` (string or null), `message` (string, human-readable), `param` (string or null). ### `session.idle_timeout` No user speech for the idle window (5 minutes). The session closes after this event. Seconds without detected user speech. ### `session.max_duration_reached` The session reached its server-enforced wall-clock cap. The session closes after this event. The enforced maximum session duration, in seconds. *** ## Data types ### Conversation items All items have `id`, `object: "realtime.item"`, and a `type`: | Type | Fields | Notes | | ---------------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | `message` (role `user`) | `content[]` of `input_text` / `input_audio`, `status` | Client-created messages carry `input_text` content; spoken turns carry audio with the transcript filled in as it completes. | | `message` (role `assistant`) | `content[]` of `text` / `audio` (with `transcript`), `status` | | | `function_call` | `call_id`, `name`, `arguments`, `status`, `executor` (`"client"`) | A tool call for your client to execute. | | `function_call_output` | `call_id`, `output`, `status` | Sent by the client with the tool's result. | ### Response object ```json theme={null} { "id": "resp_...", "object": "realtime.response", "status": "completed", "status_details": null, "output": [], "usage": null, "metadata": null } ``` * `status` is `in_progress` (on `response.created`), then `completed` or `cancelled` (on `response.done`). * `status_details` is defined in the schema (`{"type": "cancelled", "reason": "turn_detected" | "client_cancelled"}`, `{"type": "incomplete", "reason": "max_output_tokens" | "content_filter"}`, `{"type": "failed", "error": {"code", "message"}}`) but is currently always sent as `null`. * `usage` (`{"total_tokens", "input_tokens", "output_tokens"}`) is currently always sent as `null`. * `metadata` is echoed on `response.created` only; `response.done` carries `metadata: null`. ## Related The events you send that trigger these. Decode `response.output_audio.delta` in the format you configured. Handling `speech_started` as the barge-in signal. Acting on `response.function_call_arguments.done`.
*** # Create a video Source: https://docs.boson.ai/api-reference/videos/create-a-video /openapi.json post /v1/videos Create an avatar talking-head video (async). Returns the Video object with `status: "queued"`; poll `GET /v1/videos/{video_id}` and download the rendered MP4 from `GET /v1/videos/{video_id}/content`. Provide a reference image plus exactly one driving input — `input` (audio-to-video) or `input_tts` (text-to-video). The body may be JSON or `multipart/form-data` (upload `ref_image` / `input` as raw files). # Create a video (streaming) Source: https://docs.boson.ai/api-reference/videos/create-a-video-streaming /openapi.json post /v1/videos/stream Same request body as `POST /v1/videos`, but the response body IS the live fragmented-MP4 (fMP4) byte stream — frames arrive as they are generated, so playback can start before the clip is complete. The video id rides back in the `X-Video-Id` header; the full MP4 is stored too, so a later `GET /v1/videos/{video_id}/content` works. # Download content Source: https://docs.boson.ai/api-reference/videos/download-content /openapi.json get /v1/videos/{video_id}/content Download the rendered video. Returns the MP4 bytes (`variant=video`, the default). `404` until the video is `completed`. # Retrieve a video Source: https://docs.boson.ai/api-reference/videos/retrieve-a-video /openapi.json get /v1/videos/{video_id} Retrieve the Video object (status / progress). Always JSON — the rendered MP4 is downloaded from `GET /v1/videos/{video_id}/content`. # Authentication Source: https://docs.boson.ai/authentication Authenticate Boson API requests with a Bearer token. ## Get an API key Create keys from the [API Keys](https://www.boson.ai/workspace/api-key) page in your workspace: click **Create API Key**, name it, and copy the key (format `bai-xxxx`). New to Boson AI? Follow [Set up your account](/set-up-your-account) for the full checklist — account, key, and free trial credit. Treat API keys like passwords. Never commit keys to source control, never log them, and never embed them in client-side code shipped to browsers or mobile apps. Building a browser or mobile Realtime client? Your trusted server must exchange its long-lived key for a short-lived [client secret](/api-reference/realtime/client-secrets). ## Store keys safely Read the key from an environment variable, secret manager, or deployment config. Recommended patterns: * **Quick local test**: set the key in your shell for the current session: ```bash theme={null} export BOSON_API_KEY=bai-xxxx ``` * **Local development**: use a `.env` file loaded by `direnv`, `dotenv`, or your shell. Add `.env` to `.gitignore`. * **Servers**: inject through your platform's secret store (AWS Secrets Manager, GCP Secret Manager, Vercel env vars, Fly secrets, Kubernetes Secrets). * **CI**: store as a masked CI secret. Avoid printing the value in build logs. ## Sending a server-side request From a trusted server, pass the key in the `Authorization` header on every API request. Browser and mobile Realtime clients must use a short-lived client secret instead. ```bash cURL theme={null} curl https://api.boson.ai/v1/audio/speech \ -H "Authorization: Bearer $BOSON_API_KEY" ``` ```python Python theme={null} import os from openai import OpenAI client = OpenAI( base_url="https://api.boson.ai/v1", api_key=os.environ["BOSON_API_KEY"], ) ``` ```typescript TypeScript theme={null} import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.boson.ai/v1", apiKey: process.env.BOSON_API_KEY, }); ``` ## Common errors The API returns `401 Unauthorized` with `error.type: "authentication_error"` and `error.code: "invalid_api_key"` when there is a problem with your API key — missing, malformed, or revoked. Check the `Authorization: Bearer` header and the key in your [workspace](https://www.boson.ai/workspace/api-key). A `429` with `insufficient_quota` is a billing problem, not a key problem — the key is fine but the account has no available balance. Claim your [free trial credit](/free-trial-credit) or add credits in [workspace billing](https://www.boson.ai/workspace/billing/overview). See [Common error types](/api-reference/errors) for the full reference. # Free trial credit Source: https://docs.boson.ai/free-trial-credit What the Boson AI free trial credit covers, how to claim it, and what happens when it runs out. New Boson AI accounts get **\$10 in free trial credit** to use on any Boson API — Higgs Realtime, Higgs TTS 3, and Higgs Avatar. No payment method is required. ## Claim your credit Trial credit is not added to your balance automatically. Claim it once from either: * the banner on the [API Keys](https://www.boson.ai/workspace/api-key) page, or * the banner in [billing overview](https://www.boson.ai/workspace/billing/overview). The claim banner in your workspace shows the exact amount available to your account. Until you claim, your account has no active billing balance and API calls fail with a `429` `insufficient_quota` error (Realtime sessions receive an `error` event and close with code `4429`). See [Common error types](/api-reference/errors). ## Scope and expiry * Trial credit works on **all Boson APIs** — there is no per-product restriction. * Trial credit expires **12 months after you claim it**. * Usage draws down your balance at the standard [pricing](/pricing) rates. ## When it runs out When your balance reaches zero, requests are refused with the same `insufficient_quota` error until you [add credits](/account-billing/payment-and-credits). Your account and API keys are unaffected — service resumes once you add credits. To avoid interruption, add a payment method and turn on auto-reload before the credit runs out. You can watch your balance and spend anytime in [billing overview](https://www.boson.ai/workspace/billing/overview). # Inputs and limits Source: https://docs.boson.ai/models/higgs-avatar/input-options Choose audio or text input, upload local assets, and check Higgs Avatar sizes, limits, and common errors. Every Avatar request needs a `ref_image` plus exactly one driving input: | Input | Use when | Request field | | -------------- | ------------------------------------------- | ------------- | | Existing audio | You already have the voice performance | `input` | | Text-to-speech | Higgs TTS should generate the driving voice | `input_tts` | `ref_image` and `input` accept an HTTPS URL, data URI, base64 value, or multipart file upload. ## Drive with an audio clip Pass the driving voice in `input`. The avatar lip-syncs and moves to the audio, and the audio duration determines the video length. ```json theme={null} { "model": "higgs-avatar", "ref_image": "https://docs.boson.ai/public/avatar/sample.jpg", "input": "https://docs.boson.ai/public/audio/sample.mp3", "size": "640x640" } ``` For a complete create, poll, and download example, follow the [quickstart](/models/higgs-avatar/overview#quickstart). ## Drive with text Put a [Higgs TTS request](/models/higgs-tts/overview) under `input_tts`. Choose a reusable `voice`, or supply TTS voice-cloning inputs. Do not send `input` in the same request. ```bash cURL theme={null} curl -s https://api.boson.ai/v1/videos \ -H "Authorization: Bearer $BOSON_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "higgs-avatar", "ref_image": "https://docs.boson.ai/public/avatar/sample.jpg", "input_tts": { "model": "higgs-tts-3", "input": "Hello, world.", "voice": "default" }, "size": "640x640" }' ``` ```python Python theme={null} import os import requests response = requests.post( "https://api.boson.ai/v1/videos", headers={"Authorization": f"Bearer {os.environ['BOSON_API_KEY']}"}, json={ "model": "higgs-avatar", "ref_image": "https://docs.boson.ai/public/avatar/sample.jpg", "input_tts": { "model": "higgs-tts-3", "input": "Hello, world.", "voice": "default", }, "size": "640x640", }, ) response.raise_for_status() print(response.json()) ``` ```typescript TypeScript theme={null} const response = await fetch("https://api.boson.ai/v1/videos", { method: "POST", headers: { Authorization: `Bearer ${process.env.BOSON_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "higgs-avatar", ref_image: "https://docs.boson.ai/public/avatar/sample.jpg", input_tts: { model: "higgs-tts-3", input: "Hello, world.", voice: "default", }, size: "640x640", }), }); if (!response.ok) throw new Error(await response.text()); console.log(await response.json()); ``` ## Upload local files Use `multipart/form-data` to upload a local image and audio clip without base64 encoding. When driving with text, pass `input_tts` as a JSON-string form field. ```bash cURL theme={null} curl -s https://api.boson.ai/v1/videos \ -H "Authorization: Bearer $BOSON_API_KEY" \ -F model=higgs-avatar \ -F size=480x640 \ -F ref_image=@face.png \ -F input=@voice.wav ``` ```python Python theme={null} import os import requests with open("face.png", "rb") as ref_image, open("voice.wav", "rb") as input_audio: response = requests.post( "https://api.boson.ai/v1/videos", headers={"Authorization": f"Bearer {os.environ['BOSON_API_KEY']}"}, data={ "model": "higgs-avatar", "size": "480x640", }, files={ "ref_image": ref_image, "input": input_audio, }, ) response.raise_for_status() print(response.json()) ``` ```typescript TypeScript theme={null} import { readFile } from "node:fs/promises"; const form = new FormData(); form.set("model", "higgs-avatar"); form.set("size", "480x640"); form.set("ref_image", new Blob([await readFile("face.png")]), "face.png"); form.set("input", new Blob([await readFile("voice.wav")]), "voice.wav"); const response = await fetch("https://api.boson.ai/v1/videos", { method: "POST", headers: { Authorization: `Bearer ${process.env.BOSON_API_KEY}`, }, body: form, }); if (!response.ok) throw new Error(await response.text()); console.log(await response.json()); ``` ## Output sizes Choose the aspect ratio that best matches the reference image so the subject is not cropped. | `size` | Aspect ratio | Use for | | --------- | ------------ | ------------------- | | `640x640` | 1:1 | Square, the default | | `640x480` | 4:3 | Landscape | | `480x640` | 3:4 | Portrait | ## Input limits | Field | Limit | | ----------------- | --------------------------------------------------------- | | `ref_image` | PNG, JPEG, or WebP; inline base64 or data URI up to 10 MB | | `input` | AAC, WAV, MP3, FLAC, or Opus; up to 60 seconds | | `input_tts.input` | Up to 5,000 characters | Assets supplied by URL are fetched by the service and are not subject to the inline payload-size limit. ## Common request errors | Error code | What to check | | ---------------------- | ------------------------------------------------- | | `invalid_image_format` | Use a supported reference-image format | | `payload_too_large` | Reduce an inline image or audio payload | | `audio_too_long` | Keep the driving audio at or below 60 seconds | | `input_too_long` | Shorten the text sent to Higgs TTS | | `invalid_size` | Use one of the documented output-size presets | | `model_not_found` | Use the published `higgs-avatar` model identifier | For failed jobs, log the video ID and complete error object so the request can be traced. Use the same audio-driven or text-driven body with the streaming endpoint. Look up every request field and additional option. # Overview Source: https://docs.boson.ai/models/higgs-avatar/overview Generate talking-head video from a still image and a driving voice or text. Higgs Avatar turns a single still image into talking-head video with lip sync, head motion, and expression aligned to the driving voice. ```text Model theme={null} higgs-avatar ``` ```text Video jobs endpoint (POST) theme={null} https://api.boson.ai/v1/videos ``` ```text Streaming endpoint (POST) theme={null} https://api.boson.ai/v1/videos/stream ``` ## What you can build | Capability | Input | Output | | ---------------------- | -------------------------------------- | ------------------------------------------------------ | | Audio-driven video | Still image and an existing audio clip | Rendered MP4 or fragmented-MP4 stream | | Text-driven video | Still image and a Higgs TTS request | Rendered MP4 or fragmented-MP4 stream | | Instant avatar cloning | One PNG, JPEG, or WebP image | A new talking-head performance without avatar training | “Streaming” describes how generated video is delivered. Higgs Avatar does not manage a persistent, two-way conversation. ## Quickstart This quickstart creates an audio-driven video from Boson AI-hosted sample assets. A successful run saves the finished result as `out.mp4`. ### Before you begin You need: * A [Boson API key](/authentication) stored in `BOSON_API_KEY` * Available credit — new accounts must claim their [free trial credit](/free-trial-credit) before their first API call * cURL and `jq`, Python 3.10 with `requests`, or Node.js 18 or later ```bash theme={null} export BOSON_API_KEY="bai-xxxx" ``` ### Create, wait, and download Avatar video generation is asynchronous: 1. `POST /v1/videos` creates a job and returns a Video object with an `id`. 2. `GET /v1/videos/{video_id}` reports progress until the job is `completed` or `failed`. 3. `GET /v1/videos/{video_id}/content` downloads the rendered MP4. ```bash cURL theme={null} # 1. Create the video. VIDEO_ID=$(curl -fsS https://api.boson.ai/v1/videos \ -H "Authorization: Bearer $BOSON_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "higgs-avatar", "ref_image": "https://docs.boson.ai/public/avatar/sample.jpg", "input": "https://docs.boson.ai/public/audio/sample.mp3", "size": "640x640" }' | jq -er .id) # 2. Wait for the Video object to complete. while true; do VIDEO=$(curl -fsS "https://api.boson.ai/v1/videos/$VIDEO_ID" \ -H "Authorization: Bearer $BOSON_API_KEY") STATUS=$(printf '%s' "$VIDEO" | jq -r .status) [ "$STATUS" = "completed" ] && break if [ "$STATUS" = "failed" ]; then printf '%s' "$VIDEO" | jq .error >&2 exit 1 fi sleep 2 done # 3. Download the rendered MP4. curl -fsS "https://api.boson.ai/v1/videos/$VIDEO_ID/content" \ -H "Authorization: Bearer $BOSON_API_KEY" \ --output out.mp4 ``` ```python Python theme={null} import os import time import requests base_url = "https://api.boson.ai/v1/videos" headers = {"Authorization": f"Bearer {os.environ['BOSON_API_KEY']}"} video = requests.post( base_url, headers=headers, json={ "model": "higgs-avatar", "ref_image": "https://docs.boson.ai/public/avatar/sample.jpg", "input": "https://docs.boson.ai/public/audio/sample.mp3", "size": "640x640", }, ) video.raise_for_status() video_id = video.json()["id"] while True: status = requests.get(f"{base_url}/{video_id}", headers=headers) status.raise_for_status() current = status.json() if current["status"] == "completed": break if current["status"] == "failed": raise RuntimeError(current.get("error")) time.sleep(2) content = requests.get(f"{base_url}/{video_id}/content", headers=headers) content.raise_for_status() with open("out.mp4", "wb") as file: file.write(content.content) print(f"Saved out.mp4 from video {video_id}") ``` ```typescript TypeScript theme={null} import { writeFile } from "node:fs/promises"; const baseURL = "https://api.boson.ai/v1/videos"; const authorization = `Bearer ${process.env.BOSON_API_KEY}`; const createResponse = await fetch(baseURL, { method: "POST", headers: { Authorization: authorization, "Content-Type": "application/json", }, body: JSON.stringify({ model: "higgs-avatar", ref_image: "https://docs.boson.ai/public/avatar/sample.jpg", input: "https://docs.boson.ai/public/audio/sample.mp3", size: "640x640", }), }); if (!createResponse.ok) throw new Error(await createResponse.text()); const video = await createResponse.json(); while (true) { const statusResponse = await fetch(`${baseURL}/${video.id}`, { headers: { Authorization: authorization }, }); if (!statusResponse.ok) throw new Error(await statusResponse.text()); const current = await statusResponse.json(); if (current.status === "completed") break; if (current.status === "failed") throw new Error(JSON.stringify(current.error)); await new Promise((resolve) => setTimeout(resolve, 2000)); } const contentResponse = await fetch(`${baseURL}/${video.id}/content`, { headers: { Authorization: authorization }, }); if (!contentResponse.ok) throw new Error(await contentResponse.text()); await writeFile("out.mp4", Buffer.from(await contentResponse.arrayBuffer())); console.log(`Saved out.mp4 from video ${video.id}`); ``` The integration is working when the job reaches `completed` and `out.mp4` opens in your video player. ### Job states `GET /v1/videos/{video_id}` returns the current Video object. | Status | Meaning | Client action | | ------------- | --------------------------- | ---------------------------------- | | `queued` | The job is waiting to start | Continue polling with backoff | | `in_progress` | Video generation is running | Continue polling | | `completed` | The MP4 is ready | Download content | | `failed` | Generation stopped | Inspect the Video object's `error` | `GET /v1/videos/{video_id}/content` downloads the rendered MP4 after completion. It returns `404` while content is not yet available. ## Next steps Generate from audio or text, upload local files, and check supported sizes and limits. Start playback before the complete video is rendered with fragmented MP4. Look up the generated video endpoints and field-level request details. ## Try it in the playground The fastest way to preview the model is the [Boson AI playground](https://www.boson.ai/workspace). Pick an avatar, paste text, and press play. ## When to use another Higgs API Use Higgs TTS 3 when your output is audio and you do not need a talking-head video. Use Higgs Realtime for a persistent, interruptible audio or text session with tool calling. # Streaming video Source: https://docs.boson.ai/models/higgs-avatar/streaming-video Receive fragmented MP4 from Higgs Avatar so playback can begin before generation finishes. Use `POST /v1/videos/stream` when your application should start receiving video before the complete clip is rendered. The request body matches `POST /v1/videos`, but the response body is a fragmented-MP4 byte stream. Streaming changes delivery, not the interaction model. Higgs Avatar generates a video from supplied image and audio or text; it does not create a persistent conversational session. ## How streaming works * Video fragments arrive as they are generated. * The video ID is returned in the `X-Video-Id` response header. * The complete MP4 is stored, so it can still be downloaded later from `GET /v1/videos/{video_id}/content`. * Browsers can append fragments to a Media Source Extensions `SourceBuffer`. ## Stream to a file ```bash cURL theme={null} curl -N https://api.boson.ai/v1/videos/stream \ -H "Authorization: Bearer $BOSON_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "higgs-avatar", "ref_image": "https://docs.boson.ai/public/avatar/sample.jpg", "input": "https://docs.boson.ai/public/audio/sample.mp3", "size": "640x640" }' \ --output out.mp4 ``` ```python Python theme={null} import os import requests payload = { "model": "higgs-avatar", "ref_image": "https://docs.boson.ai/public/avatar/sample.jpg", "input": "https://docs.boson.ai/public/audio/sample.mp3", "size": "640x640", } with open("out.mp4", "wb") as file, requests.post( "https://api.boson.ai/v1/videos/stream", headers={"Authorization": f"Bearer {os.environ['BOSON_API_KEY']}"}, json=payload, stream=True, timeout=300, ) as response: response.raise_for_status() video_id = response.headers["X-Video-Id"] for chunk in response.iter_content(chunk_size=8192): if chunk: file.write(chunk) print(f"Saved out.mp4 from video {video_id}") ``` ```typescript TypeScript theme={null} import { writeFile } from "node:fs/promises"; const response = await fetch("https://api.boson.ai/v1/videos/stream", { method: "POST", headers: { Authorization: `Bearer ${process.env.BOSON_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "higgs-avatar", ref_image: "https://docs.boson.ai/public/avatar/sample.jpg", input: "https://docs.boson.ai/public/audio/sample.mp3", size: "640x640", }), }); if (!response.ok || !response.body) { throw new Error(await response.text()); } const chunks: Uint8Array[] = []; const reader = response.body.getReader(); while (true) { const { value, done } = await reader.read(); if (done) break; if (value) chunks.push(value); } await writeFile("out.mp4", Buffer.concat(chunks)); console.log(`video id: ${response.headers.get("X-Video-Id")}`); ``` Streaming is working when response bytes begin arriving before generation completes and the resulting `out.mp4` opens successfully. ## Play fragments in a browser Use the [Media Source Extensions API](https://developer.mozilla.org/en-US/docs/Web/API/Media_Source_Extensions_API) to append incoming fragmented-MP4 chunks to a `SourceBuffer`. Your application should also handle network interruption, buffer backpressure, and unsupported codecs. Use the same audio-driven or text-driven body with the streaming endpoint. Look up the endpoint contract and response details. # Audio and voices Source: https://docs.boson.ai/models/higgs-realtime/guides/audio-and-voices Choose a Realtime voice, audio codec, and sample rate. ## Voices Select the assistant's voice with `audio.output.voice`: * `"default"` – the built-in preset, always available. * A named preset voice from the [Text-to-Speech API](https://docs.boson.ai/models/higgs-tts/voices#preset-voices). * A custom voice `voice_`, created via the voices API (`POST /v1/audio/voices`). See [Custom Voices](https://docs.boson.ai/models/higgs-tts/voices#custom-voices) for more details. Any non-`"default"` voice is validated when you send [`session.update`](/api-reference/realtime/client-events#session-update); an unknown or inaccessible voice fails the update. To adjust delivery (pace, tone, energy), prompt the model via `instructions` — e.g. "Speak slowly and calmly." ```json theme={null} { "type": "session.update", "session": { "model": "higgs-realtime", "audio": { "output": { "voice": "default" } } } } ``` ## Audio format ### Supported codecs | Format | Encoding | Sample rates | | --------------------- | -------------------------- | ------------------------------------------------------------------------- | | `audio/pcm` (default) | PCM16, little-endian, mono | 8000 / 16000 / 24000 / 48000 Hz (default 24000) | | `audio/pcmu` | G.711 μ-law | 8000 Hz fixed | | `audio/opus` | Opus | 24000 Hz internal; `frame_size_ms` of 2.5, 5, 10, 20 (default), 40, or 60 | Internal processing always runs at 24 kHz, 16-bit, mono PCM. Audio travels base64-encoded inside JSON events. ### Configuration example ```json theme={null} { "type": "session.update", "session": { "model": "higgs-realtime", "audio": { "input": { "format": { "type": "audio/pcm", "rate": 16000 } }, "output": { "format": { "type": "audio/pcm", "rate": 16000 } } } } } ``` # Connections and sessions Source: https://docs.boson.ai/models/higgs-realtime/guides/connections-and-sessions Connect over WebSocket, authenticate safely, configure a session, and choose Realtime and transcription models. ## Connection and session **WebSocket endpoint:** ``` wss://api.boson.ai/v1/realtime ``` The server accepts the optional `realtime` WebSocket subprotocol. ### Session configuration Configure the session with `session.update` (field paths relative to `session`): | Parameter | Description | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `model` | The realtime model for the session: `higgs-realtime`. Optional when the connection URL carries `?model=`; an explicit value here overrides the URL. One of the two must be set. See [Model Selection](#model-selection). | | `instructions` | System prompt for agent behavior. | | `audio.output.voice` | `"default"`, a named preset, or a custom `voice_`. See [Voices](/models/higgs-realtime/guides/audio-and-voices#voices). | | `audio.input.turn_detection` | `{"type": "server_vad"}` for automatic turn taking, `{"type": "semantic_vad"}` for smarter end-of-turn judgment, or `null` for manual commit. See [Turn detection and interruptions](/models/higgs-realtime/guides/turn-detection-and-interruptions). | | `audio.input.transcription` | Enable input transcription: set `model` to `higgs-stt-3.1` (with an optional `language` hint) to receive `conversation.item.input_audio_transcription.completed` events for user audio. When unset, no transcription events are emitted. See [Model Selection](#model-selection). | | `audio.input.noise_reduction` | `{"type": "near_field"}` or `{"type": "far_field"}` to denoise input audio before the model hears it; `null` disables. | | `output_modalities` | Exactly `["audio"]` (default, spoken output) or `["text"]` (stream `response.output_text.*` instead of audio). Mixed or empty lists are rejected. | | `tools` | Array of `function` tools (JSON-schema parameters). See [Tool use](/models/higgs-realtime/guides/tool-calling). | | `tool_choice` | `"auto"` (default), `"none"`, `"required"`, or `{"type": "function", "name": ...}`. | | `temperature` | Sampling temperature (default `0.3`). | | `max_output_tokens` | Per-response cap; integer or `"inf"` (default). Ints are clamped to 4096. | | `truncation` | `"auto"` (default, smart context summarization) or `"disabled"`. See [Context management](/models/higgs-realtime/migrate-an-existing-integration#context-management). | ## Authentication Authentication supports two methods: * **API key** (server-side): pass an `Authorization: Bearer ` header when opening the WebSocket. * **Ephemeral key** (client-side, recommended for browsers): mint a short-lived key server-side via `POST /v1/realtime/client_secrets`, then pass it from the browser in the WebSocket subprotocol list as `bai-client-secret.`. Ephemeral keys start with `bai-eph-`. ```javascript theme={null} const ws = new WebSocket("wss://api.boson.ai/v1/realtime", [ "realtime", `bai-client-secret.${ephemeralKey}`, ]); ``` ### Minting an ephemeral key Ephemeral keys let browser and mobile clients connect directly without ever seeing your real API key. From your backend, call `POST /v1/realtime/client_secrets` with your API key. The body is optional — `expires_after.seconds` sets the key's TTL (10–7200 seconds, default 600): ```bash theme={null} curl -X POST https://api.boson.ai/v1/realtime/client_secrets \ -H "Authorization: Bearer $BOSON_API_KEY" \ -H "Content-Type: application/json" \ -d '{"expires_after": {"seconds": 300}}' ``` Response: ```json theme={null} { "object": "realtime.client_secret", "value": "bai-eph-", "expires_at": 1712345678, "session": { "id": "sess_ab12cd34", "object": "realtime.session" } } ``` Hand the `value` to your client, which uses it in the `bai-client-secret.` subprotocol as shown above. The key expires at `expires_at` (Unix seconds) — mint a fresh one per connection. If the key is invalid or expired, the WebSocket closes with code `3000`. ## Model selection | Model | Role | Description | | ---------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | | `higgs-realtime` | Session model | End-to-end speech-to-speech model. Consumes and produces audio directly, with built-in turn detection, barge-in handling, and tool calling. | | `higgs-stt-3.1` | Input transcription (optional) | Speech-to-text model for transcribing user audio. | ### Session model `higgs-realtime` is currently the only supported session model. Set it with the `?model=` query parameter when connecting (`wss://api.boson.ai/v1/realtime?model=higgs-realtime`), or in `session.update` (which overrides the URL): ```json theme={null} { "type": "session.update", "session": { "model": "higgs-realtime" } } ``` ### Input transcription model The session works entirely in audio — you do not need a transcription model to converse. Optionally, set `audio.input.transcription.model` to `higgs-stt-3.1` to receive text transcripts of the user's audio as `conversation.item.input_audio_transcription.completed` events (useful for captions, logging, or moderation): ```json theme={null} { "type": "session.update", "session": { "model": "higgs-realtime", "audio": { "input": { "transcription": { "model": "higgs-stt-3.1" } } } } } ``` Transcription is billed as usage of the specified model. When `transcription.model` is unset, no transcription events are emitted and no transcription charges apply. An optional `transcription.language` hint biases the transcript toward a language — pass an ISO-639-1 code (`"ja"`, `"ko"`); unrecognized values are ignored. This is a transcription hint only, not a conversation-language setting: the model detects the spoken language automatically and replies in kind, and after the first reply the transcription language follows the conversation automatically. ## WebSocket events Communication uses JSON-encoded events. Key event types: **Client → Server:** * [`session.update`](/api-reference/realtime/client-events#session-update) – Configure the session (model, voice, instructions, audio formats, tools, turn detection) * [`input_audio_buffer.append`](/api-reference/realtime/client-events#input_audio_buffer-append) – Stream base64 audio chunks (max \~1 MiB base64 per append, ≈15 s of 24 kHz PCM) * [`input_audio_buffer.commit`](/api-reference/realtime/client-events#input_audio_buffer-commit) / [`input_audio_buffer.clear`](/api-reference/realtime/client-events#input_audio_buffer-clear) – Manual turn control * [`conversation.item.create`](/api-reference/realtime/client-events#conversation-item-create) – Inject user or assistant text messages, or function-call results * [`response.create`](/api-reference/realtime/client-events#response-create) – Request a model response * [`response.cancel`](/api-reference/realtime/client-events#response-cancel) – Cancel an in-flight response **Server → Client:** * [`session.created`](/api-reference/realtime/server-events#session-created) / [`session.updated`](/api-reference/realtime/server-events#session-updated) – Session initialization and config acks * [`input_audio_buffer.speech_started`](/api-reference/realtime/server-events#input_audio_buffer-speech_started) / [`speech_stopped`](/api-reference/realtime/server-events#input_audio_buffer-speech_stopped) – Server VAD events * [`response.output_audio.delta`](/api-reference/realtime/server-events#response-output_audio-delta) – Streaming audio chunks (base64) * [`response.output_audio_transcript.delta`](/api-reference/realtime/server-events#response-output_audio_transcript-delta) – Streaming transcript of the spoken response * [`response.function_call_arguments.done`](/api-reference/realtime/server-events#response-function_call_arguments-done) – Tool invocation ready * [`response.done`](/api-reference/realtime/server-events#response-done) – Turn completion * [`error`](/api-reference/realtime/server-events#error) – Session errors See the complete event catalog and payload schemas in the Realtime API reference: [client events](/api-reference/realtime/client-events) and [server events](/api-reference/realtime/server-events). Every event you send: fields, types, and the full session configuration object. Every event you receive: session, audio, response lifecycle, tool calls, and errors. # Tool use Source: https://docs.boson.ai/models/higgs-realtime/guides/tool-calling Declare functions, execute tool calls in your application, return results, and continue the Realtime response. Declare function tools in `session.update`: ```json theme={null} { "tools": [ { "type": "function", "name": "get_weather", "description": "Get current weather for a city", "parameters": { "type": "object", "properties": { "city": { "type": "string" } }, "required": ["city"] } } ] } ``` When the model calls a tool, the server streams [`response.function_call_arguments.delta`](/api-reference/realtime/server-events#response-function_call_arguments-delta) events and finishes with [`response.function_call_arguments.done`](/api-reference/realtime/server-events#response-function_call_arguments-done), then ends the response with [`response.done`](/api-reference/realtime/server-events#response-done) carrying the completed `function_call` item (`call_id`, `name`, `arguments`). It is now your client's turn: 1. Execute the function locally with the provided arguments. 2. Return the result with [`conversation.item.create`](/api-reference/realtime/client-events#conversation-item-create) (`type: "function_call_output"`, echoing the `call_id`). 3. Send [`response.create`](/api-reference/realtime/client-events#response-create) — the model continues with the result and speaks the answer. The core handler flow is shown below. Pass the connected WebSocket as `ws`: ```python theme={null} import json TOOLS = [ { "type": "function", "name": "get_weather", "description": "Get current weather for a city", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"], }, } ] def get_weather(city: str) -> dict: ... # your implementation async def run_tool_turn(ws): # Declare the tool and ask a question that needs it. await ws.send(json.dumps({ "type": "session.update", "session": { "model": "higgs-realtime", "tools": TOOLS, "instructions": "Use the available tools when needed.", }, })) await ws.send(json.dumps({ "type": "conversation.item.create", "item": { "type": "message", "role": "user", "content": [ {"type": "input_text", "text": "What's the weather in Tokyo?"} ], }, })) await ws.send(json.dumps({"type": "response.create"})) async for msg in ws: event = json.loads(msg) if event["type"] == "response.done": # A response ending in a tool call carries the function_call item. calls = [ item for item in event["response"]["output"] if item.get("type") == "function_call" ] if not calls: break # Final spoken reply finished. call = calls[0] result = get_weather(**json.loads(call["arguments"])) # 1. Execute. await ws.send(json.dumps({ "type": "conversation.item.create", # 2. Return the result. "item": { "type": "function_call_output", "call_id": call["call_id"], "output": json.dumps(result), }, })) await ws.send( json.dumps({"type": "response.create"}) ) # 3. Continue. elif event["type"] == "response.output_audio.delta": ... # Play audio as usual. ``` # Turn detection and interruptions Source: https://docs.boson.ai/models/higgs-realtime/guides/turn-detection-and-interruptions Configure automatic or manual turns and keep the conversation aligned during barge-in. ## Turn detection Set `audio.input.turn_detection` to `{"type": "server_vad"}` for automatic turn taking, `{"type": "semantic_vad"}` for smarter end-of-turn judgment, or `null` for manual `input_audio_buffer.commit` control. With server VAD active, the model automatically responds when you stop speaking, and user speech during playback interrupts the assistant (barge-in). `server_vad` parameters: | Parameter | Default | Description | | --------------------- | ------- | ------------------------------------------- | | `threshold` | `0.55` | VAD detection threshold (0.0–1.0) | | `prefix_padding_ms` | `300` | Audio included before detected speech | | `silence_duration_ms` | `500` | Silence needed to end a turn | | `min_speech_duration` | `0.125` | Minimum speech (seconds) to start a segment | `semantic_vad` extends `server_vad` with a semantic judgment of whether the user has truly finished speaking, reducing premature cut-offs during natural pauses. ## Interruptions (barge-in) User speech during assistant playback cancels the in-flight response: audio output stops, the assistant's conversation item is truncated at the interruption point, and a new user turn begins immediately. Clients can also truncate played-back items explicitly with [`conversation.item.truncate`](/api-reference/realtime/client-events#conversation-item-truncate) (by `audio_end_ms`) so the stored transcript matches what the user actually heard. `input_audio_buffer.speech_started` is the event to listen for — stop local playback when it arrives. # LiveKit Source: https://docs.boson.ai/models/higgs-realtime/integrations/livekit LiveKit support status for Higgs Realtime, and what to use in the meantime. A first-party LiveKit integration for Higgs Realtime is coming soon. In the meantime, connect to the Realtime API directly over [WebSocket](/models/higgs-realtime/guides/connections-and-sessions), or use the [Pipecat integration](/models/higgs-realtime/integrations/pipecat). # Pipecat Source: https://docs.boson.ai/models/higgs-realtime/integrations/pipecat Use Higgs Realtime as a speech-to-speech service in a Pipecat pipeline. The `pipecat-boson` package exposes Higgs Realtime as a [Pipecat](https://docs.pipecat.ai/) speech-to-speech `LLMService`. It receives live audio or text, manages the conversation, calls tools, and streams audio or text responses. A voice pipeline does not need separate STT, LLM, and TTS services. ```text Model theme={null} higgs-realtime ``` ```text WebSocket endpoint theme={null} wss://api.boson.ai/v1/realtime ``` ## Before you begin You need: * Python 3.11 or newer * A [Boson API key](/authentication) stored in `BOSON_API_KEY` * Access to the Higgs Realtime API * An existing Pipecat application with an audio transport Keep the Boson API key on the server. Never embed it in a browser or mobile client. ## Install the package During the preview, install the package from GitHub. The repository might still be private, in which case you need a GitHub account with access: ```bash uv theme={null} uv add "pipecat-boson @ git+ssh://git@github.com/boson-ai/pipecat-boson.git" ``` ```bash pip theme={null} pip install "pipecat-boson @ git+ssh://git@github.com/boson-ai/pipecat-boson.git" ``` For HTTPS, replace the source URL with `git+https://github.com/boson-ai/pipecat-boson.git`. To develop the package or run its included example: ```bash theme={null} git clone git@github.com:boson-ai/pipecat-boson.git cd pipecat-boson uv sync --extra dev ``` To use a local checkout from another `uv` project: ```bash theme={null} uv add --editable ../pipecat-boson ``` The package supports `pipecat-ai>=1.4.0,<2`. ## Configure the connection Set the API key, WebSocket endpoint, and model ID in your server environment: ```bash theme={null} export BOSON_API_KEY=bai-xxxx export BOSON_REALTIME_URL=wss://api.boson.ai/v1/realtime export BOSON_REALTIME_MODEL=higgs-realtime ``` Create the realtime service: ```python theme={null} import os from pipecat_boson.realtime import BosonRealtimeLLMService llm = BosonRealtimeLLMService( url=os.environ["BOSON_REALTIME_URL"], api_key=os.environ["BOSON_API_KEY"], model=os.environ["BOSON_REALTIME_MODEL"], voice="default", instructions="You are a concise and helpful voice assistant.", ) ``` ## Add the service to a pipeline The following example assumes that `transport` is an existing Pipecat audio transport: ```python theme={null} from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.worker import PipelineParams, PipelineWorker from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.llm_response_universal import ( LLMContextAggregatorPair, ) from pipecat.workers.runner import WorkerRunner async def run_bot(transport, llm): context = LLMContext() user_aggregator, assistant_aggregator = LLMContextAggregatorPair( context, realtime_service_mode=True, ) pipeline = Pipeline( [ transport.input(), user_aggregator, llm, transport.output(), assistant_aggregator, ] ) worker = PipelineWorker( pipeline, params=PipelineParams( enable_metrics=True, ), ) runner = WorkerRunner() await runner.add_workers(worker) await runner.run() ``` `realtime_service_mode=True` lets the context aggregators follow the server-driven turn lifecycle. Do not add separate STT or TTS services around `BosonRealtimeLLMService`. Call `run_bot(transport, llm)` from your application's async entry point. Higgs Realtime responds after server VAD detects the end of a user turn. If the assistant should speak first, queue an `LLMRunFrame` after the client is ready, as demonstrated by the included browser example. ## Run the browser example From the repository checkout created above, copy the example environment file: ```bash theme={null} cp .env.example .env ``` Set `BOSON_API_KEY`, `BOSON_REALTIME_URL`, and `BOSON_REALTIME_MODEL` in `.env`, then start the example: ```bash WebRTC theme={null} uv run --extra webrtc \ python examples/pipecat_boson_realtime_agent.py \ -t webrtc \ --host 127.0.0.1 \ --port 7860 ``` ```bash WebSocket theme={null} uv run --extra webrtc \ python examples/pipecat_boson_realtime_agent.py \ -t websocket \ --host localhost \ --port 7860 ``` Open `http://localhost:7860` and connect your microphone. Use the WebSocket transport if WebRTC ICE cannot reach the server, and select **WebSocket** in the page before connecting. Both commands use the `webrtc` extra because it also installs the Pipecat runner used by the browser example. ## Receive user transcripts Set an input transcription model to receive finalized user transcripts as Pipecat `TranscriptionFrame` objects. See [Model selection](/models/higgs-realtime/guides/connections-and-sessions#model-selection) for what transcription adds to a session: ```python theme={null} llm = BosonRealtimeLLMService( url=os.environ["BOSON_REALTIME_URL"], api_key=os.environ["BOSON_API_KEY"], model=os.environ["BOSON_REALTIME_MODEL"], input_audio_transcription={ "model": "higgs-stt-3.1", "language": "en", }, ) ``` Omitting `input_audio_transcription`, passing `None`, or passing a dictionary without a non-empty `model` suppresses client-facing user transcript events. Higgs Realtime still understands the audio and can respond. ## Call Python functions Declare an async Python function with typed arguments and return its result through `result_callback`: ```python theme={null} from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.services.llm_service import FunctionCallParams async def get_weather( params: FunctionCallParams, location: str, ) -> None: """Get the current weather for a location. Args: location: City or place name. """ await params.result_callback( { "location": location, "condition": "sunny", "temperature_c": 22, } ) tools = [get_weather] llm = BosonRealtimeLLMService( url=os.environ["BOSON_REALTIME_URL"], api_key=os.environ["BOSON_API_KEY"], model=os.environ["BOSON_REALTIME_MODEL"], instructions="Use get_weather when the user asks about weather.", tools=tools, ) context = LLMContext(tools=tools) ``` Pass the same tool list to the service and the context. The service advertises and registers the handlers for the Higgs Realtime session, while `LLMContext` keeps the tool definitions with the conversation state. After the function completes, Higgs Realtime continues the response with its result. For the underlying event flow, see [Tool use](/models/higgs-realtime/guides/tool-calling). ## Configure turn detection Server VAD is enabled by default. It detects the end of the user's turn, creates a response, and interrupts an active response when the user starts speaking. For most voice agents, keep the default settings — see [Turn detection and interruptions](/models/higgs-realtime/guides/turn-detection-and-interruptions) for what each parameter does. Override the thresholds only when the default behavior does not fit the application: ```python theme={null} turn_detection = { "type": "server_vad", "prefix_padding_ms": 300, "silence_duration_ms": 500, "threshold": 0.55, } llm = BosonRealtimeLLMService( url=os.environ["BOSON_REALTIME_URL"], api_key=os.environ["BOSON_API_KEY"], turn_detection=turn_detection, ) ``` Higgs Realtime also supports OpenAI-compatible semantic VAD, which judges whether the user has truly finished speaking: ```python theme={null} semantic_turn_detection = { "type": "semantic_vad", } llm = BosonRealtimeLLMService( url=os.environ["BOSON_REALTIME_URL"], api_key=os.environ["BOSON_API_KEY"], turn_detection=semantic_turn_detection, ) ``` ## Use text-only output Pass `output_modalities=["text"]` when constructing the service. Text-only sessions emit streamed `LLMTextFrame` objects and no audio frames. The service supports exactly one session output modality: `["audio"]` or `["text"]`. Mixed output modalities and per-response modality overrides are not supported. ## Handle session events Use Pipecat service event handlers to observe the Higgs Realtime session lifecycle: ```python theme={null} def register_session_handlers(llm): @llm.event_handler("on_session_created") async def on_session_created(service, event): print("Session:", event.session.id) @llm.event_handler("on_session_terminated") async def on_session_terminated(service, event_type, event): print("Session terminated:", event_type) ``` Call `register_session_handlers(llm)` before starting `WorkerRunner`. The integration reports terminal session events but does not close the Pipecat transport automatically. Keep `on_session_created` handlers fast. Session setup waits for this handler to return. `on_session_terminated` receives [`session.idle_timeout`](/api-reference/realtime/server-events#session-idle_timeout) or [`session.max_duration_reached`](/api-reference/realtime/server-events#session-max_duration_reached). ## Supported options Connection options: | Parameter | Default | Description | | --------- | --------------------------- | ------------------------------------------------------ | | `url` | Required | Higgs Realtime WebSocket endpoint. | | `api_key` | Required for the hosted API | Boson API key sent as a Bearer token. | | `model` | `"higgs-realtime"` | Realtime model ID sent when the session is configured. | Optional session settings supported by Higgs Realtime: | Parameter | Default | Description | | ------------------------------------ | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `voice` | `"default"` | Voice preset or voice ID used for audio output. See [Audio and voices](/models/higgs-realtime/guides/audio-and-voices#voices). | | `instructions` | Helpful assistant prompt | System instructions used to initialize the conversation. | | `output_modalities` | `["audio"]` | Exactly `["audio"]` or `["text"]`. | | `temperature` | `0.7` | Sampling temperature used for model responses. | | `max_output_tokens` | `"inf"` | Maximum response tokens. Numeric values are capped at `4096`. | | `tools` | Not set | Python functions or Pipecat-compatible tool definitions. | | `tool_choice` | `"auto"` | Tool selection behavior used when tools are available. | | `turn_detection` | Server VAD | OpenAI-compatible `server_vad` or `semantic_vad` configuration. | | `input_audio_transcription` | Not set | Transcription dictionary. A non-empty `model` enables client-facing user transcript events. | | `input_audio_transcription_model` | `""` | Convenience option for the transcription model. | | `input_audio_transcription_language` | `None` | Convenience option for the transcription language. | | `input_audio_noise_reduction` | Not set | OpenAI-compatible `{"type": "near_field"}` or `{"type": "far_field"}` input noise reduction setting. The corresponding type string is also accepted. | | `truncation` | `"auto"` | `"auto"` enables smart context summarization when the selected model publishes a context limit; `"disabled"` turns it off. | This Pipecat integration sends and receives 24 kHz PCM audio. ## Next steps Understand the session settings these options map to. Look up the underlying protocol, event catalog, and payload schemas. Transports, processors, and the rest of the Pipecat framework. # Migrate an existing integration Source: https://docs.boson.ai/models/higgs-realtime/migrate-an-existing-integration Move an OpenAI Realtime integration to Higgs Realtime and review the protocol compatibility differences. If you have an existing application built on the OpenAI Realtime API, switching requires only a few changes: update the WebSocket URL, swap your API key, and select the model in `session.update`. If you are using the official OpenAI SDK, point the client at the Boson endpoint and supply your Boson API key: ```python theme={null} import asyncio import os from openai import AsyncOpenAI # Before (OpenAI) # client = AsyncOpenAI(api_key=os.environ["OPENAI_API_KEY"]) # After (Boson AI) client = AsyncOpenAI( api_key=os.environ["BOSON_API_KEY"], base_url="https://api.boson.ai/v1", ) async def main(): async with client.realtime.connect(model="higgs-realtime") as conn: await conn.session.update(session={ "model": "higgs-realtime", "instructions": "You are a helpful assistant.", # ... rest of your session config }) # ... rest of your application code asyncio.run(main()) ``` If you connect directly via WebSocket, change the URL and `Authorization` header: ```python theme={null} import os import websockets # Before (OpenAI) # url = "wss://api.openai.com/v1/realtime?model=gpt-realtime" # headers = {"Authorization": f"Bearer {os.environ['OPENAI_API_KEY']}"} # After (Boson AI) url = "wss://api.boson.ai/v1/realtime?model=higgs-realtime" headers = {"Authorization": f"Bearer {os.environ['BOSON_API_KEY']}"} async with websockets.connect(url, additional_headers=headers) as ws: # Most event handling can be reused after reviewing the compatibility differences below ... ``` Use `higgs-realtime` in place of `gpt-realtime`. As on OpenAI, the `?model=` query parameter in the connection URL selects the model; you can also set (or override) it in `session.update`: ```python theme={null} await ws.send(json.dumps({ "type": "session.update", "session": { "model": "higgs-realtime", # ... the rest of your existing session config } })) ``` If neither the URL nor the first `session.update` names a model, the update fails with an error. * Check the [compatibility notes](#openai-realtime-api-compatibility) below: a few OpenAI session fields are inert or rejected here, and `output_modalities` must be exactly one modality. * Revisit your system prompt: instructions written to patch quirks of another model usually aren't needed — start from a short, direct prompt and add back only what proves necessary. ## OpenAI Realtime API Compatibility The event protocol is compatible with OpenAI's GA Realtime API: most OpenAI Realtime clients work by changing the endpoint to `wss://api.boson.ai/v1/realtime` and supplying a Boson API key. The differences are listed below, roughly in the order a migrating client will hit them. * **WebSocket only.** OpenAI's WebRTC and SIP transports are not available — no `/v1/realtime/calls` endpoints, no `output_audio_buffer.*` events, no DTMF events. WebRTC support is in progress. * **Model selection works as on OpenAI.** The `?model=` query parameter selects the model; `session.model` in a `session.update` overrides it. If neither is set, the first `session.update` fails. Use `higgs-realtime` in place of `gpt-realtime`. * **`session.created` acknowledges your first `session.update`.** OpenAI emits `session.created` immediately on connect; here the server sends nothing until you send `session.update`. Don't wait for `session.created` before configuring the session. `conversation.created` is never emitted. * **Realtime sessions only.** `session.type` is always `"realtime"`; OpenAI's transcription-only sessions (`"type": "transcription"`) are not supported. * **Ephemeral keys:** `POST /v1/realtime/client_secrets` works the same way (`expires_after.seconds`, 10–7200, default 600), but a `session` object in the request body is ignored — configure the session over the WebSocket after connecting. Keys are prefixed `bai-eph-` rather than `ek_`. * `output_modalities` must be exactly one of `["audio"]` or `["text"]` — mixed output is not supported. * **Audio formats:** `audio/pcm` accepts `rate` 8000 / 16000 / 24000 / 48000 (OpenAI: 24000 only), and `audio/opus` is available as an extension. `audio/pcmu` works as on OpenAI; `audio/pcma` (G.711 A-law) is not supported. * **Voices:** OpenAI voice names (`alloy`, `marin`, `cedar`, …) don't exist here — use `"default"`, a Boson AI preset, or a custom `voice_` (see [Voices](/models/higgs-realtime/guides/audio-and-voices#voices)). Non-default voices are validated at `session.update` time, so an OpenAI voice name fails the update. * **Input transcription:** set `transcription.model` to `higgs-stt-3.1`; OpenAI transcription models are not available. `language` works as on OpenAI; OpenAI's `prompt`, `keywords`, `languages`, and `delay` transcription options are ignored. * **Tools:** `function` tools only. MCP tools (`"type": "mcp"`), tool approvals, and the `mcp_*` events are not supported. * **`temperature`** is accepted at the session level (default `0.3`) — an extension over OpenAI's GA schema. * **Turn detection:** `server_vad` and `semantic_vad` both exist, with differences: the `server_vad` threshold defaults to `0.55` (OpenAI: `0.5`), and `turn_detection.min_speech_duration` (minimum speech length in seconds before a segment starts, default `0.125`) is an extension. `create_response`, `interrupt_response`, and `eagerness` are accepted but have no effect — a response is always generated on end of turn, and barge-in always interrupts. `idle_timeout_ms` is rejected with an error if set. * **Rejected session fields** (fail the `session.update` if set): `include`, `prompt`, `tracing`. * **`truncation`** supports the `"auto"`/`"disabled"` string forms only (no `retention_ratio` object), and the mechanism differs — see [Context management](#context-management) below. * `input_audio_buffer.append` accepts at most 1 MiB of base64 audio per event (OpenAI: 15 MiB) — stream microphone audio in small chunks. * `conversation.item.create` supports text content only: `input_audio` and `input_image` message contents are skipped, and `system`-role messages are rejected. User messages use `input_text` content and assistant messages use `text` content (OpenAI's `output_text` type is not recognized). `function_call` and `function_call_output` items work as on OpenAI. * Input transcription arrives only as `conversation.item.input_audio_transcription.completed` — the `.delta`, `.failed`, and `.segment` (diarization) events are never emitted. * Also never emitted: `conversation.item.done`, `rate_limits.updated`, and `input_audio_buffer.timeout_triggered`. Don't gate client logic on receiving them. * Extra event types not in OpenAI's schema: `response.output_audio_transcript.length`, `conversation.context.summarized`, `session.idle_timeout`, and `session.max_duration_reached`. Make sure your client tolerates unknown event types rather than failing on them. * `response.done` reports `status` `"completed"` or `"cancelled"` only, and its `usage` and `status_details` fields are currently always `null` (OpenAI populates per-response token usage). Response `metadata` is echoed on `response.created` only, not on `response.done`. ### Context management With `truncation: "auto"` (default), long conversations are summarized in the background before they exceed the model's context window: older messages are condensed into a summary item (originals stay retrievable) and the server emits `conversation.context.summarized` with the affected `summarized_item_ids`. Set `truncation: "disabled"` to opt out. This differs from OpenAI's Realtime API, which drops old messages instead of summarizing them — and note that `conversation.context.summarized` is not an OpenAI event type, so make sure your client tolerates it rather than failing on unknown events (the official OpenAI SDK may not surface or parse it). ### Session limits Session limits are enforced server-side and cannot be changed via `session.update`: * **Max session duration:** the server sends `session.max_duration_reached` (carrying `max_duration_sec`) and closes when a session reaches its wall-clock cap. * **Idle timeout:** after 5 minutes without detected user speech, the server sends `session.idle_timeout` (carrying `seconds_idle`) and closes. * **Quota refusals:** if the account's billing entitlement is refused mid-session, the server sends an `error` event with the upstream message (`type: "insufficient_quota"`) and closes the WebSocket with code `4429`. Check your balance and add credits in [workspace billing](https://www.boson.ai/workspace/billing/overview). ### WebRTC WebRTC transport support is in progress. WebSocket is the supported transport today. # Overview Source: https://docs.boson.ai/models/higgs-realtime/overview Build realtime voice assistants over WebSocket with streaming audio, text, turn detection, and tool calling. The Realtime API enables real-time bidirectional voice communication over WebSocket (WebRTC support is in progress). Stream audio and text both ways for voice assistants, phone agents, and interactive voice systems: the model listens and speaks natively in a single full-duplex session — no separate transcription or synthesis steps to manage. ```text Model theme={null} higgs-realtime ``` ```text WebSocket endpoint theme={null} wss://api.boson.ai/v1/realtime ``` ## Features * **Full-duplex voice** — one persistent session streams audio and text in both directions; the model listens and speaks natively. * **Turn detection and barge-in** — `server_vad` and `semantic_vad` detect end of turn and reply automatically; interruptions always stop the model mid-utterance. See [Turn detection](/models/higgs-realtime/guides/turn-detection-and-interruptions). * **Tool calling** — `function` tools with JSON-schema parameters, called mid-conversation. See [Tool use](/models/higgs-realtime/guides/tool-calling). * **Voices** — the `default` voice, Boson AI presets, or your own cloned `voice_`. See [Audio & voices](/models/higgs-realtime/guides/audio-and-voices). * **Input transcription** — live user-speech transcripts via `higgs-stt-3.1`. * **Text mode** — set `output_modalities` to `["text"]` to stream text instead of spoken audio. * **OpenAI-compatible protocol** — the event protocol is compatible with OpenAI's GA Realtime API; most integrations move with config changes. See [Migrate an existing integration](/models/higgs-realtime/migrate-an-existing-integration). * **Browser and mobile clients** — trusted servers mint short-lived [client secrets](/api-reference/realtime/client-secrets) so keys never ship to devices. ## Start building Every step from a fresh account to a working voice assistant. Every client and server event, close codes, and session fields. Production voice-agent frameworks with Higgs Realtime built in. Move an existing OpenAI Realtime integration over. Six parts in TypeScript and React, from zero to a working browser voice assistant — with a git checkpoint after every part. # Quickstart Source: https://docs.boson.ai/models/higgs-realtime/quickstart Every step from a fresh account to a working Higgs Realtime voice assistant. This quickstart walks the full path to a working voice app: set up your account, then build a browser voice assistant with the official tutorial — or hear your first spoken reply with one short Python script. One session does everything — the model listens and speaks natively, with no separate transcription or synthesis steps to manage. **Looking for an easier path?** [LiveKit](/models/higgs-realtime/integrations/livekit) and [Pipecat](/models/higgs-realtime/integrations/pipecat) have Higgs Realtime support built in and handle the WebSocket, audio, and interruption plumbing for you — often the fastest route to a production voice agent. Already running on OpenAI's Realtime API? [Migrate your existing integration](/models/higgs-realtime/migrate-an-existing-integration) with a few config changes. This page covers the raw WebSocket path — the best way to learn how the API actually works. ## How it works Your app holds a single WebSocket session and exchanges JSON events with the model: ```mermaid theme={null} sequenceDiagram participant App as Your app participant RT as wss://api.boson.ai/v1/realtime App->>RT: session.update — voice, turn detection, instructions App->>RT: conversation.item.create — a user message App->>RT: response.create RT-->>App: response.output_audio.delta (audio chunks) RT-->>App: response.output_audio_transcript.delta (text) RT-->>App: response.done ``` That's the whole loop. When you stream microphone audio instead of text, turn detection makes the last step automatic — the model replies on its own when you stop speaking, no `response.create` needed. ## Before you begin Whichever path you take below, you need: * A [Boson API key](/authentication) stored in `BOSON_API_KEY` * Available credit — new accounts must claim their [free trial credit](/free-trial-credit) before their first API call New to Boson AI? [Set up your account](/set-up-your-account) walks through all of it. ## Start with the tutorial The easiest and most practical way to build with Higgs Realtime is the official tutorial: six parts that take you from zero to a working browser voice assistant, with a working checkpoint to fall back on after every part. TypeScript and React, verified against live sessions. Clone it, add your key, and build. The parts arrive in the same order a real voice service comes together: keep your API key off the browser with ephemeral keys, open the first connection, stream a live microphone with turn detection and interruptions, rebuild a correct transcript from the event stream, add tool calling, and shape the system prompt. Finish it and you have touched every building block of a voice app once. Building server-side instead — a phone agent, a voice pipeline — or want the smallest possible first step? The rest of this page plays a spoken reply with one short Python script. ## Hear a reply in Python You need Python 3.10 or later with the `websockets` and `sounddevice` packages, and speakers or headphones — the reply plays as audio: ```bash theme={null} export BOSON_API_KEY="bai-xxxx" pip install websockets sounddevice ``` Connect, configure the session, send a text message, and play the model's spoken reply: ```python theme={null} import asyncio import base64 import json import os import sounddevice as sd import websockets async def voice_agent(): # Speaker output matching the default audio format: 24 kHz, 16-bit, mono PCM speaker = sd.RawOutputStream(samplerate=24000, channels=1, dtype="int16") speaker.start() async with websockets.connect( "wss://api.boson.ai/v1/realtime", additional_headers={"Authorization": f"Bearer {os.environ['BOSON_API_KEY']}"} ) as ws: # Configure session await ws.send(json.dumps({ "type": "session.update", "session": { "model": "higgs-realtime", "instructions": "You are a helpful assistant.", "audio": { "input": {"turn_detection": {"type": "server_vad"}}, "output": {"voice": "default"} } } })) # Send a text message await ws.send(json.dumps({ "type": "conversation.item.create", "item": {"type": "message", "role": "user", "content": [{"type": "input_text", "text": "Hello!"}]} })) await ws.send(json.dumps({"type": "response.create"})) # Play audio responses as they stream in async for msg in ws: event = json.loads(msg) if event["type"] == "response.output_audio.delta": speaker.write(base64.b64decode(event["delta"])) elif event["type"] == "response.output_audio_transcript.done": print("Assistant:", event["transcript"]) elif event["type"] == "response.done": break speaker.stop() asyncio.run(voice_agent()) ``` You should hear the assistant speak its reply out loud, then see its transcript printed. The reply arrives as a stream of `response.output_audio.delta` events (base64 24 kHz 16-bit mono PCM) alongside `response.output_audio_transcript.delta` text, ending with `response.done`. To talk instead of type, stream microphone audio with `input_audio_buffer.append` — with `server_vad` turn detection the model detects when you stop speaking and responds on its own, no `response.create` needed. Building for the browser or mobile? Don't ship your API key: mint a short-lived key with `POST /v1/realtime/client_secrets` and connect with the `bai-client-secret.` subprotocol (see [Authentication](/models/higgs-realtime/guides/connections-and-sessions#authentication)). The [tutorial](#start-with-the-tutorial) above builds this flow end to end. ## It didn't work? `sounddevice` plays through your system's default output device. Check that a working output device is selected and unmuted. The transcript print (`Assistant: …`) confirms the model replied even if playback failed. Invalid API key (or an invalid/expired ephemeral key). Check `BOSON_API_KEY` is set in the shell running the script and matches a key in your [workspace](https://www.boson.ai/workspace/api-key). See [Authentication](/authentication). A billing refusal — your key is fine, but the account has no available balance. New accounts must [claim their free trial credit](/free-trial-credit) first; otherwise [add credits](/account-billing/payment-and-credits) in [workspace billing](https://www.boson.ai/workspace/billing/overview). See [Common error types](/api-reference/errors). ## Go deeper Session configuration, limits, and lifecycle. Audio formats, preset voices, and custom voices. `server_vad`, `semantic_vad`, and barge-in. Let the model call your functions mid-conversation. Production voice-agent frameworks with Higgs Realtime built in. The protocol is OpenAI Realtime compatible — most integrations move with config changes. # Languages Source: https://docs.boson.ai/models/higgs-tts/languages Higgs TTS 3 supports 102 languages with single-digit WER/CER. Language is auto-detected from the input text, so you do not need to specify it in the API. For cloned voices, match the language of the input text and reference audio when possible. To generate speech with a particular accent, such as British English, use a reference clip with that accent. ## Supported languages Coverage is measured by word or character error rate (WER/CER) — **lower is better**. Languages are grouped by quality tier and ordered alphabetically within each tier. ### WER/CER under 5 Polished, production-quality output (85 languages). 🇿🇦 Afrikaans · 🇸🇦🇪🇬 Arabic · 🇦🇲 Armenian · 🇮🇳 Assamese · 🇪🇸 Asturian · 🇦🇿 Azerbaijani · 🇷🇺 Bashkir · 🇪🇸 Basque · 🇧🇾 Belarusian · 🇧🇩🇮🇳 Bengali · 🇧🇦 Bosnian · 🇧🇬 Bulgarian · 🇪🇸 Catalan · 🇵🇭 Cebuano · 🇮🇶 Central Kurdish · 🇨🇳 Chinese · 🇭🇷 Croatian · 🇨🇿 Czech · 🇩🇰 Danish · 🇳🇱🇧🇪 Dutch · 🇷🇺 Eastern Mari · 🇺🇸🇬🇧🇦🇺 English · 🌐 Esperanto · 🇪🇪 Estonian · 🇫🇮 Finnish · 🇫🇷🇨🇦 French · 🇪🇸 Galician · 🇬🇪 Georgian · 🇩🇪🇦🇹 German · 🇬🇷 Greek · 🇮🇳 Gujarati · 🇭🇹 Haitian Creole · 🇳🇬 Hausa · 🇮🇱 Hebrew · 🇮🇳 Hindi · 🇭🇺 Hungarian · 🇮🇩 Indonesian · 🇮🇹 Italian · 🇯🇵 Japanese · 🇮🇩 Javanese · 🇮🇳 Kannada · 🇰🇿 Kazakh · 🇰🇷 Korean · 🇷🇼 Kinyarwanda · 🇰🇬 Kyrgyz · 🇱🇻 Latvian · 🇨🇩 Lingala · 🇱🇹 Lithuanian · 🇰🇪 Luo · 🇲🇰 Macedonian · 🇲🇾🇮🇩 Malay · 🇮🇳 Malayalam · 🇲🇹 Maltese · 🇳🇿 Māori · 🇮🇳 Marathi · 🇲🇳 Mongolian · 🇳🇵 Nepali · 🇳🇴 Norwegian · 🇫🇷 Occitan · 🇮🇷🇦🇫 Persian · 🇵🇱 Polish · 🇵🇹🇧🇷 Portuguese · 🇷🇴 Romanian · 🇷🇺 Russian · 🇿🇦 Sepedi · 🇷🇸 Serbian · 🇿🇼 Shona · 🇸🇰 Slovak · 🇸🇮 Slovene · 🇪🇸🇲🇽 Spanish · 🇹🇿🇰🇪 Swahili · 🇸🇪 Swedish · 🇵🇭 Tagalog · 🇹🇯 Tajik · 🇮🇳🇱🇰 Tamil · 🇮🇳 Telugu · 🇹🇭 Thai · 🇹🇷 Turkish · 🇺🇦 Ukrainian · 🇵🇰🇮🇳 Urdu · 🇨🇳 Uyghur · 🇺🇿 Uzbek · 🇻🇳 Vietnamese · 🇿🇦 Xhosa · 🇿🇦 Zulu ### WER/CER between 5 and 10 Usable, but less polished (17 languages). 🇦🇱 Albanian · 🇲🇼🇿🇲 Chichewa/Nyanja · 🇮🇳🇵🇰 Eastern Punjabi · 🇺🇬 Ganda · 🇮🇸 Icelandic · 🇮🇪 Irish · 🇩🇿 Kabyle · 🇨🇻 Kabuverdianu · 🇰🇪 Kamba · 🇻🇦 Latin · 🇱🇺 Luxembourgish · 🇪🇹🇰🇪 Oromo · 🇦🇫 Pashto · 🇵🇰🇮🇳 Sindhi · 🇸🇴 Somali · 🇦🇴 Umbundu · 🇬🇧 Welsh # Overview Source: https://docs.boson.ai/models/higgs-tts/overview Chat-native text-to-speech with streaming, 100 languages, instant voice cloning, and inline emotion and style control. ```text Model theme={null} higgs-tts-3 ``` ```text Speech endpoint (POST) theme={null} https://api.boson.ai/v1/audio/speech ``` Need a model that listens and responds across turns? Use [Higgs Realtime](/models/higgs-realtime/overview). Higgs TTS 3 renders speech from text; it does not manage a conversation. ## Features * **Chat-native, low-latency streaming** — begin speaking before the full input is finalized. * **100 languages** — single-digit WER/CER coverage. See [Languages](./languages). * **Instant voice cloning** — zero-shot from a short reference clip and its transcript. See [Voices](./voices). * **Inline control tags** — shape emotion, style, prosody, and sound effects with `<|emotion:…|>`, `<|style:…|>`, `<|prosody:…|>`, and `<|sfx:…|>`. See [Tags](./tags). ## Try it in the playground The fastest way to hear the model is the [playground](https://www.boson.ai/workspace). Pick a voice, paste text, and press play. ## Generate speech with the API You need a [Boson API key](/authentication) stored in `BOSON_API_KEY`, and available credit — new accounts must claim their [free trial credit](/free-trial-credit) before their first API call. Set the key in your shell for the current session: ```bash theme={null} export BOSON_API_KEY=bai-xxxx ``` A minimal request needs `Authorization`, `model`, and `input`. Everything else is optional. ```bash cURL theme={null} curl https://api.boson.ai/v1/audio/speech \ -H "Authorization: Bearer $BOSON_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "higgs-tts-3", "input": "Hello, this is a test." }' \ --output out.mp3 ``` ```python Python theme={null} import os import requests resp = requests.post( "https://api.boson.ai/v1/audio/speech", headers={"Authorization": f"Bearer {os.environ['BOSON_API_KEY']}"}, json={ "model": "higgs-tts-3", "input": "Hello, this is a test.", }, ) resp.raise_for_status() with open("out.mp3", "wb") as f: f.write(resp.content) ``` ```typescript TypeScript theme={null} import { writeFile } from "node:fs/promises"; const res = await fetch("https://api.boson.ai/v1/audio/speech", { method: "POST", headers: { Authorization: `Bearer ${process.env.BOSON_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "higgs-tts-3", input: "Hello, this is a test.", }), }); await writeFile("out.mp3", Buffer.from(await res.arrayBuffer())); ``` ## Use a preset voice Use `voice` to choose a preset speaker. ```bash cURL theme={null} curl https://api.boson.ai/v1/audio/speech \ -H "Authorization: Bearer $BOSON_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "higgs-tts-3", "input": "Hello, this is a test.", "voice": "jake" }' \ --output out.mp3 ``` ```python Python theme={null} import os import requests resp = requests.post( "https://api.boson.ai/v1/audio/speech", headers={"Authorization": f"Bearer {os.environ['BOSON_API_KEY']}"}, json={ "model": "higgs-tts-3", "input": "Hello, this is a test.", "voice": "jake", }, ) resp.raise_for_status() with open("out.mp3", "wb") as f: f.write(resp.content) ``` ```typescript TypeScript theme={null} import { writeFile } from "node:fs/promises"; const res = await fetch("https://api.boson.ai/v1/audio/speech", { method: "POST", headers: { Authorization: `Bearer ${process.env.BOSON_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "higgs-tts-3", input: "Hello, this is a test.", voice: "jake", }), }); await writeFile("out.mp3", Buffer.from(await res.arrayBuffer())); ``` See [Voices](./voices#preset-voices) for more preset speakers and samples. ## Use reference audio Use `ref_audio` to clone a voice from a short reference clip. Passing the audio transcript through `ref_text` can often improve generated audio quality. ```bash cURL theme={null} curl https://api.boson.ai/v1/audio/speech \ -H "Authorization: Bearer $BOSON_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "higgs-tts-3", "input": "Hello, this is a test.", "ref_audio": "https://docs.boson.ai/public/audio/sample.mp3", "ref_text": "Same voice, same words, and uh, a completely different presence. I was built for chat native voice, real-time, expressive, and controllable." }' \ --output out.mp3 ``` ```python Python theme={null} import os import requests resp = requests.post( "https://api.boson.ai/v1/audio/speech", headers={"Authorization": f"Bearer {os.environ['BOSON_API_KEY']}"}, json={ "model": "higgs-tts-3", "input": "Hello, this is a test.", "ref_audio": "https://docs.boson.ai/public/audio/sample.mp3", "ref_text": "Same voice, same words, and uh, a completely different presence. I was built for chat native voice, real-time, expressive, and controllable.", }, ) resp.raise_for_status() with open("out.mp3", "wb") as f: f.write(resp.content) ``` ```typescript TypeScript theme={null} import { writeFile } from "node:fs/promises"; const res = await fetch("https://api.boson.ai/v1/audio/speech", { method: "POST", headers: { Authorization: `Bearer ${process.env.BOSON_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "higgs-tts-3", input: "Hello, this is a test.", ref_audio: "https://docs.boson.ai/public/audio/sample.mp3", ref_text: "Same voice, same words, and uh, a completely different presence. I was built for chat native voice, real-time, expressive, and controllable.", }), }); await writeFile("out.mp3", Buffer.from(await res.arrayBuffer())); ``` To clone from a **local** file, either encode local file as base64 string or send as \`multipart/form-data. Below code shows the latter. ```bash cURL theme={null} curl https://api.boson.ai/v1/audio/speech \ -H "Authorization: Bearer $BOSON_API_KEY" \ -F model=higgs-tts-3 \ -F input="Hello, this is a test." \ -F ref_audio=@voice.wav \ -F ref_text="Transcript of the reference clip." \ --output out.mp3 ``` ```python Python theme={null} import os import requests with open("voice.wav", "rb") as ref_audio: resp = requests.post( "https://api.boson.ai/v1/audio/speech", headers={"Authorization": f"Bearer {os.environ['BOSON_API_KEY']}"}, data={ "model": "higgs-tts-3", "input": "Hello, this is a test.", "ref_text": "Transcript of the reference clip.", }, files={"ref_audio": ref_audio}, ) resp.raise_for_status() with open("out.mp3", "wb") as f: f.write(resp.content) ``` ```typescript TypeScript theme={null} import { readFile, writeFile } from "node:fs/promises"; const form = new FormData(); form.set("model", "higgs-tts-3"); form.set("input", "Hello, this is a test."); form.set("ref_text", "Transcript of the reference clip."); form.set("ref_audio", new Blob([await readFile("voice.wav")]), "voice.wav"); const res = await fetch("https://api.boson.ai/v1/audio/speech", { method: "POST", headers: { Authorization: `Bearer ${process.env.BOSON_API_KEY}`, }, body: form, }); await writeFile("out.mp3", Buffer.from(await res.arrayBuffer())); ``` You must own the right to clone the voice. See [Voices](./voices#reference-voice) for best practices and reusable custom voices. ## Fine-grained control Inline tags control emotion, style, prosody, and sound effects in the generated audio. Add them to `input`, and the model adjusts the surrounding speech. For example:
| Sample input | Sample audio | | ------------------------------------------------------------------------------------ | ------------------------- | | `<\|emotion:enthusiasm\|>Welcome to the show! <\|prosody:pause\|>Let's get started!` | `voice: "jake"`
See [Tags](./tags) for the complete list and sample audio. ## Streaming response When `stream: true`, set `response_format: "pcm"`. ```bash cURL theme={null} curl -N https://api.boson.ai/v1/audio/speech \ -H "Authorization: Bearer $BOSON_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "higgs-tts-3", "input": "Hello, this is a streaming PCM test.", "response_format": "pcm", "stream": true }' ``` ```python Python theme={null} import os import requests BASE_URL = "https://api.boson.ai/v1" API_KEY = os.environ["BOSON_API_KEY"] payload = { "model": "higgs-tts-3", "input": "Hello, this is a streaming PCM test.", "voice": "default", "response_format": "pcm", "stream": True, } # The response body is a raw 16-bit / 24kHz / mono PCM byte stream — collect chunks directly. pcm = bytearray() with requests.post( f"{BASE_URL}/audio/speech", headers={ "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json", }, json=payload, stream=True, timeout=180, ) as r: r.raise_for_status() for chunk in r.iter_content(chunk_size=4096): if chunk: # first non-empty chunk == time-to-first-audio pcm.extend(chunk) with open("out.pcm", "wb") as f: f.write(pcm) ``` ```typescript TypeScript theme={null} import { writeFile } from "node:fs/promises"; const res = await fetch("https://api.boson.ai/v1/audio/speech", { method: "POST", headers: { Authorization: `Bearer ${process.env.BOSON_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "higgs-tts-3", input: "Hello, this is a streaming PCM test.", response_format: "pcm", stream: true, }), }); if (!res.ok || !res.body) throw new Error(`HTTP ${res.status}`); // Raw 16-bit / 24kHz / mono PCM byte stream — no "data:" lines, just collect the bytes. const reader = res.body.getReader(); const pcmChunks: Uint8Array[] = []; while (true) { const { value, done } = await reader.read(); if (done) break; if (value) pcmChunks.push(value); // first chunk == time-to-first-audio } await writeFile("out.pcm", Buffer.concat(pcmChunks)); ``` ## API reference Full request body: ```jsonc theme={null} { "model": "higgs-tts-3", "input": "Text to synthesize.", "voice": "default", "response_format": "mp3", "stream": false, "ref_audio": "base64 | data URI | URL", "ref_text": "Transcript of the reference audio.", } ``` See the [API reference](/api-reference/audio/create-a-speech) for field details and additional options. ## Alternative ways to use the model Beyond the hosted API, you can run the model yourself: * **Hugging Face** — open model weights at [bosonai/higgs-tts-v3-4b](https://huggingface.co/bosonai/higgs-tts-v3-4b). * **SGLang** — serve the model locally for high-throughput inference. See the [Higgs TTS cookbook](https://sgl-project.github.io/sglang-omni/cookbook/higgs_tts.html). # Tags Source: https://docs.boson.ai/models/higgs-tts/tags Inline control tokens that shape emotion, style, prosody, and sound effects inside the input text. Inline tags control delivery at the token level. Insert them anywhere in `input`, and the model adjusts the surrounding speech. For example: ``` <|emotion:enthusiasm|>Welcome to the show! <|prosody:pause|>Let's get started! ``` Tags fall into four categories: * **emotion**: `<|emotion:…|>`, such as elation, fear, anger. * **style**: `<|style:…|>`, such as shouting, whispering. * **sound effects**: `<|sfx:…|>`, such as cough, sneeze. * **prosody**: `<|prosody:…|>`, including speed, pause, pitch, and expressiveness. Recommended usage: * Lead the turn with delivery tokens. Emotion, style, speed, pitch, and expressiveness tags set how the entire turn is delivered, so place them at the start of the input before any text. Positional tokens are the exception: `<|prosody:pause|>` and `<|prosody:long_pause|>` go exactly where the break should fall, and each `<|sfx:…|>` goes right before the sound it triggers. * Pair every sound effect with onomatopoeia. A `<|sfx:…|>` token works best when the matching written sound follows immediately, such as `<|sfx:laughter|>Haha`, `<|sfx:sigh|>Uh`, or `<|sfx:sneeze|>Achoo`. The written cue helps the model realize the sound effect.
## Emotion | Tag | Effect | Sample input | Sample audio | | ----------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------- | ------------ | | `<\|emotion:elation\|>` | Elation / joy | `<\|emotion:elation\|>This is the best news ever, I am absolutely thrilled!` |
# Voices Source: https://docs.boson.ai/models/higgs-tts/voices Use preset voices, reference audio, or reusable custom voices with Higgs TTS 3. Choose a preset voice, clone a voice from reference audio, or create a reusable custom voice. ## Preset voices
| Voice | Style | Sample input | Sample audio | | --------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | | `chloe` | A friendly and clear female voice with an engaging, informative tone and a standard American accent. | `<\|emotion:amusement\|>Hi, I'm Chloe Adams! <\|emotion:enthusiasm\|>I love showing you the little tricks that make everything click so much faster. <\|prosody:pause\|> Ready? Let's jump right in and make this easy and fun.` |
Use `voice` to select a preset speaker. ```bash cURL theme={null} curl https://api.boson.ai/v1/audio/speech \ -H "Authorization: Bearer $BOSON_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "higgs-tts-3", "input": "Hello, this is a test.", "voice": "berlinda" }' \ --output out.mp3 ``` ```python Python theme={null} import os import requests resp = requests.post( "https://api.boson.ai/v1/audio/speech", headers={"Authorization": f"Bearer {os.environ['BOSON_API_KEY']}"}, json={ "model": "higgs-tts-3", "input": "Hello, this is a test.", "voice": "berlinda" }, ) resp.raise_for_status() with open("out.mp3", "wb") as f: f.write(resp.content) ``` ```typescript TypeScript theme={null} import { writeFile } from "node:fs/promises"; const res = await fetch("https://api.boson.ai/v1/audio/speech", { method: "POST", headers: { Authorization: `Bearer ${process.env.BOSON_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "higgs-tts-3", input: "Hello, this is a test.", voice: "oliver", }), }); await writeFile("out.mp3", Buffer.from(await res.arrayBuffer())); ``` ## Reference voice Clone a voice instantly with `ref_audio` and `ref_text`. ```bash cURL theme={null} curl https://api.boson.ai/v1/audio/speech \ -H "Authorization: Bearer $BOSON_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "higgs-tts-3", "input": "Hello, this is a test.", "ref_audio": "https://docs.boson.ai/public/audio/sample.mp3", "ref_text": "Same voice, same words, and uh, a completely different presence. I was built for chat native voice, real-time, expressive, and controllable." }' \ --output out.mp3 ``` ```python Python theme={null} import os import requests resp = requests.post( "https://api.boson.ai/v1/audio/speech", headers={"Authorization": f"Bearer {os.environ['BOSON_API_KEY']}"}, json={ "model": "higgs-tts-3", "input": "Hello, this is a test.", "ref_audio": "https://docs.boson.ai/public/audio/sample.mp3", "ref_text": "Same voice, same words, and uh, a completely different presence. I was built for chat native voice, real-time, expressive, and controllable." }, ) resp.raise_for_status() with open("out.mp3", "wb") as f: f.write(resp.content) ``` ```typescript TypeScript theme={null} import { writeFile } from "node:fs/promises"; const res = await fetch("https://api.boson.ai/v1/audio/speech", { method: "POST", headers: { Authorization: `Bearer ${process.env.BOSON_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "higgs-tts-3", input: "Hello, this is a test.", "ref_audio": "https://docs.boson.ai/public/audio/sample.mp3", "ref_text": "Same voice, same words, and uh, a completely different presence. I was built for chat native voice, real-time, expressive, and controllable." }), }); await writeFile("out.mp3", Buffer.from(await res.arrayBuffer())); ``` `ref_audio` can be a URL or the base64-encoded bytes of a local audio file. Supported formats are `wav`, `mp3`, `opus`, `pcm` and `flac`. We recommend 5-30 seconds of clean speech, with no music or background voices. Although `ref_text` is optional, we recommend providing a verbatim transcript of the reference audio, including filler words. You must own the right to clone the voice. ## Custom voices Custom voices work like reference voices, but you can reuse the returned `voice` ID instead of sending `ref_audio` on every request. First, create a voice ID from reference audio and text. Then pass that ID to `voice`, just like a preset voice. ```bash cURL theme={null} # 1) Create a reusable voice from reference audio — returns a voice_id. curl https://api.boson.ai/v1/audio/voices \ -H "Authorization: Bearer $BOSON_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "ref_audio": "https://docs.boson.ai/public/audio/sample.mp3", "ref_text": "Same voice, same words, and uh, a completely different presence. I was built for chat native voice, real-time, expressive, and controllable.", "title": "My Voice" }' # => {"voice_id": "voice_abc123...", "title": "My Voice", "created_at": "...", "ref_text": "..."} # 2) Reuse it on any request by passing the voice_id to "voice". curl https://api.boson.ai/v1/audio/speech \ -H "Authorization: Bearer $BOSON_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "higgs-tts-3", "input": "Using my saved voice.", "voice": "voice_abc123..." }' \ --output out.mp3 ``` ```python Python theme={null} import os import requests API = "https://api.boson.ai/v1" headers = {"Authorization": f"Bearer {os.environ['BOSON_API_KEY']}"} # 1) Create a reusable voice from reference audio — returns a voice_id. created = requests.post( f"{API}/audio/voices", headers=headers, json={ "ref_audio": "https://docs.boson.ai/public/audio/sample.mp3", "ref_text": "Same voice, same words, and uh, a completely different presence. I was built for chat native voice, real-time, expressive, and controllable.", "title": "My Voice", }, ) created.raise_for_status() voice_id = created.json()["voice_id"] # 2) Reuse it on any request by passing the voice_id to "voice". resp = requests.post( f"{API}/audio/speech", headers=headers, json={ "model": "higgs-tts-3", "input": "Using my saved voice.", "voice": voice_id, }, ) resp.raise_for_status() with open("out.mp3", "wb") as f: f.write(resp.content) ``` ```typescript TypeScript theme={null} import { writeFile } from "node:fs/promises"; const API = "https://api.boson.ai/v1"; const headers = { Authorization: `Bearer ${process.env.BOSON_API_KEY}`, "Content-Type": "application/json", }; // 1) Create a reusable voice from reference audio — returns a voice_id. const created = await fetch(`${API}/audio/voices`, { method: "POST", headers, body: JSON.stringify({ ref_audio: "https://docs.boson.ai/public/audio/sample.mp3", ref_text: "Same voice, same words, and uh, a completely different presence. I was built for chat native voice, real-time, expressive, and controllable.", title: "My Voice", }), }); const { voice_id } = await created.json(); // 2) Reuse it on any request by passing the voice_id to "voice". const res = await fetch(`${API}/audio/speech`, { method: "POST", headers, body: JSON.stringify({ model: "higgs-tts-3", input: "Using my saved voice.", voice: voice_id, }), }); await writeFile("out.mp3", Buffer.from(await res.arrayBuffer())); ``` `ref_audio` accepts a URL or the base64-encoded bytes of a local file. Reusing the same audio returns the same `voice_id`, so creating a voice is safe to repeat. # Welcome to Boson AI Source: https://docs.boson.ai/overview Choose the Higgs API for live conversation, generated speech, or talking-avatar video. Boson AI provides three developer APIs for interactive voice, generated speech, and avatar video. Start with the outcome you need—the products use different inputs, outputs, and interaction models. ## What do you want to build? Use Higgs Realtime for a persistent session that listens, handles interruptions and tools, and streams a response. Convert text into natural speech for playback, files, or an audio stream. No conversational session required. Generate talking-head video from a still image and driving audio or text. ## Compare the APIs | Product | Input | Output | Interaction model | Best for | | ------------------------------------------------- | ------------------------------ | ----------------------- | ---------------------------------- | ---------------------------------------------- | | [Higgs Realtime](/models/higgs-realtime/overview) | Live audio or text | Streaming audio or text | Persistent, two-way session | Voice agents, interruptions, and tool use | | [Higgs TTS 3](/models/higgs-tts/overview) | Text | Generated audio | One request or audio stream | Narration, voiceovers, and speech playback | | [Higgs Avatar](/models/higgs-avatar/overview) | Still image plus audio or text | Generated video | Video job or fragmented MP4 stream | Presenters, characters, and talking-head video | ## Start building Create an account, get an API key, and claim your free trial credit. Play your first streamed Higgs Realtime response with Python. # Pricing Source: https://docs.boson.ai/pricing Usage-based pricing for Higgs Realtime, Higgs TTS 3, and Higgs Avatar. All Boson APIs are usage-based and **prepaid**: you [add credits](/account-billing/payment-and-credits), and usage draws down your balance. New accounts start with [\$10 in free trial credit](/free-trial-credit). The canonical rate card, with interactive cost estimators, is at [boson.ai/pricing](https://www.boson.ai/pricing). The rates below mirror it. ## Higgs Realtime Token-billed; the per-minute figures are estimates reflecting common usage — actual cost is calculated from tokens consumed. | Billing event | Rate | | ------------------------ | ------------------ | | Audio input (estimated) | \$0.0023 / min | | Audio output (estimated) | \$0.014 / min | | Input tokens | \$0.75 / 1M tokens | | Cached input tokens | \$0.25 / 1M tokens | | Output tokens | \$4.50 / 1M tokens | | Transcription input | \$0.0025 / min | ## Higgs TTS 3 | Billing event | Rate | | --------------- | ----------------------- | | Text input | \$0.015 / 1K characters | | Generated audio | Free | ## Higgs Avatar | Resolution | Rate | | ------------------ | ------------ | | 640 × 480 | \$0.07 / min | | 480 × 640 | \$0.07 / min | | 640 × 640 | \$0.09 / min | | Audio + text input | Free | Usage estimates exclude applicable taxes. ## Next steps Create an account, claim your free credit, and make a first call. Track spend and set a monthly cap. # Set up your account Source: https://docs.boson.ai/set-up-your-account Create a Boson AI account, get an API key, claim your free trial credit, and make your first call. Everything you need before your first API call, in order. The whole flow takes a few minutes and does not require a payment method. Go to [boson.ai/workspace](https://www.boson.ai/workspace) and sign in with Google or an email verification code. Complete the account setup and verification steps. Open the [API Keys](https://www.boson.ai/workspace/api-key) page, click **Create API Key**, and give the key a descriptive name. Copy the key when it is shown — Boson API keys use the format `bai-xxxx`. Store it securely and never commit it to source control. See [Authentication](/authentication) for storage patterns and how requests are authenticated. New accounts get **\$10 in free trial credit**, but it is not added automatically — claim it from the banner on the [API Keys](https://www.boson.ai/workspace/api-key) page or in [billing overview](https://www.boson.ai/workspace/billing/overview). No payment method is required. Don't skip this step. API calls from an account without claimed credit or a positive balance fail with a `429` `insufficient_quota` error. See [Free trial credit](/free-trial-credit). Pick the quickstart for what you're building: Hear a live spoken reply over WebSocket. Generate speech from text. Create a talking-head video. When you're ready to go beyond the trial credit, [add a payment method and credits](/account-billing/payment-and-credits), turn on auto-reload so service never stops mid-project, and [set a monthly spend cap](/account-billing/usage-and-limits) if you want a hard ceiling.