- Native endpoint:
wss://api.pyai.com/v1/omni - OpenAI-realtime-compatible alias:
wss://api.pyai.com/v1/realtime?model=pyai-omni-realtime - Deprecated alias:
wss://api.pyai.com/v2/omni/chat(works during the deprecation window; new integrations MUST use/v1/omni) - Scope:
omni:session(or theomni:*wildcard) - Status: GA
contracts/omni-asyncapi.yaml.
Field stability. Connect params, auth, the
configure frame, the
kb_endpoint callback, close codes, and metering are stable. Server→client
lifecycle event payloads below document the confirmed envelope; individual
fields marked provisional may gain keys, branch on the event type/event
name and ignore unknown fields. The official SDKs track these for you.1. Connect
Omni is zero-state, there is nothing to create first. The session is
authorized by your key’s organization; the agent’s behavior travels in the
first
configure frame (below), not a pre-built record.
2. Auth
Browsers can’t setAuthorization on a WebSocket upgrade, so pass the key as a
subprotocol (browser-safe):
?api_key=pyai_live_... to the URL. The
key is validated at the edge and swapped for the internal engine credential, the
customer key never reaches the engine. Don’t put the key in any other query param.
3. Audio frames
Send microphone audio as binary WebSocket messages in the negotiatedformat/rate (PCM16 little-endian), each prefixed with the 0x01 type
tag. Receive the agent’s speech the same way, 0x01-prefixed binary frames you
strip and play out as they arrive. Send frames continuously; the engine handles
turn detection and barge-in server-side.
4. Configure frame
Omni is stateless on PyAI. Immediately after the upgrade, the client sends one JSONconfigure control frame carrying the agent’s behavior for this call.
Control frames are keyed on type.
Fields: live vs roadmap
Agent profiles (POST /v1/agents)
Store persona, voice, greeting message, recording disclosure, and tools once;
connect with session_label={agent_id} so the engine loads them from your
stored agent profile (no need to repeat greeting in configure unless overriding).
consent_line → greeting (turn 0) → conversation.
Full walkthrough: Agent greeting messages · REST: POST /v1/agents.
4a. Function calling (tools[])
Omni supports function calling on the live engine. Declare tools in the
configure frame:
endpoint on the tool. When the brain selects a
tool, the engine emits:
{ "type": "tool_result", "call_id": "…", "error": "…" }.
Timeouts: tools are load-bearing (unlike kb_endpoint grounding).
Default per-tool budget is ~5 s (up to ~15 s). Long-running calls may trigger
a brief spoken filler while the engine waits. Results over ~6 KB are truncated.
Engine-POST (optional): add "endpoint": "https://…" on a tool definition.
Register reusable webhook tools via GET /v1/tools.
5. kb_endpoint grounding callback
If you set kb_endpoint, the engine calls your endpoint once per user turn to
fetch grounding facts. This call comes from PyAI’s engine, not the browser.
Request (engine → your endpoint):
context string and/or structured passages both work; keep it
small and fast.
Budget: the call has a hard ~300 ms timeout and is fail-open, on timeout
or any error the engine proceeds with empty facts and never blocks the
turn. Treat it as best-effort augmentation; keep it well under budget.
There is no documented retry of the callback, design it as a single
best-effort call.
session_label is how you route per-call context (e.g. look up
the caller’s chart/profile by the tag you connected with). The legacy agent_id
key may also appear in the request body for back-compat.6. Session lifecycle events (server → client)
On connect you receive a greeting/hello frame followed bysession_started,
then turn/transcript/barge-in events interleaved with binary audio, ending in
session_end.
7. Control frames (client → server)
8. Close codes
The server uses standard WebSocket close codes plus PyAI-specific application codes. Treat4xxx-class application closes as non-retryable (fix the
request); treat 1011-class closes as retryable with backoff.
A malformed
session_label/agent_id is rejected before the upgrade as
400 invalid_agent_id (an HTTP error, not a WS close).
9. Reconnect & retry
Recommended pattern:- Retry on
1011and4429with exponential backoff; do not retry4401/4403(fix the key/scope first). - Keep per-call state (the
session_label, persona/context, a short running summary) in your backend so a reconnect can re-primeconfigure/kb_endpointand continue gracefully.
10. Metering
Omni sessions meter asomni.minutes by session wall-clock duration, billed
per second (no minimum; an empty/failed call bills nothing). The billed quantity
is duration-minutes (fractional) × the Omni list rate.
Realtime WebSocket sessions do not carry an x-pyai-units response header
(that’s HTTP-only), reconcile realtime usage from your call records and usage
data.
11. Migration from /v2/omni/chat
- Change the URL from
/v2/omni/chatto/v1/omni. - Rename the
agentquery param tosession_label(theagent_id/agentaliases still work). - Keep the same
pyai-key.<key>subprotocol auth, unchanged. - The bridge-only HTTP surfaces (
/v2/omni/health,/v2/omni/calls…) are retired; there is no/v1/omniHTTP equivalent.
See also
Authentication
Key handling and the WS subprotocol.
Errors & limits
Rate, concurrency, and the error catalog.
Telephony audio (8 kHz)
μ-law ↔ PCM16 at 8 kHz for phone legs.
Language support
What’s GA vs roadmap per language.