# AGENTS.md - PyAI website guidance

Use this guide when building against or editing the PyAI website.

## Positioning

- PyAI is the telephony-built, OpenAI-compatible voice AI stack for production
  phone agents.
- **Lead in this order, never with price:** (1) quality (telephony-grade
  accuracy, natural voices, reliable turn-taking), (2) latency (~390 ms turn-taking;
  Speak first audio in tens of ms), (3) ease / drop-in (OpenAI-compatible, one
  socket, live in an afternoon), (4) telephony purpose-built (8 kHz μ-law native,
  one speech-to-speech model, not seven stitched boxes). Predictable economics
  ($0.05/min all-in) is supporting evidence, mentioned *after* the value above.
- Treat Hear and Speak as primitives and on-ramps. Lead public GTM with Omni API,
  Agents, Trace, and Cast.
- Keep competitor copy respectful and specific: compare quality, latency,
  architecture, and migration effort first; price last.

## Developer Links

- Docs (canonical): https://docs.pyai.com
- API reference: https://docs.pyai.com/api-reference
- OpenAPI spec: https://api.pyai.com/openapi.json
- Agent index: https://api.pyai.com/llms.txt
- Website llms.txt: https://pyai.com/llms.txt

## Billing Language

- AI products bill per second by default and round once at the invoice line.
- Managed telephony is the explicit exception: connected minutes bill on a
  1-minute pulse because carrier economics are different.
- Do not describe PyAI as merely cheap, and never lead with price. The
  positioning is honest, quality-first production voice infrastructure that also
  happens to sit at the real cost curve (one engine, not four markups).

## Product Names

Use customer-facing product names only: Hear, Speak, Omni, Agents, Trace, Recap,
Cue, Telephony, and Cast.
# Building against PyAI - a guide for AI agents

PyAI is a telephony-grade voice AI platform. This file tells coding agents how to
integrate correctly. The machine-readable source of truth is the OpenAPI spec at
`https://api.pyai.com/openapi.json` - fetch it; never invent endpoints.

## Base URL & auth
- REST base: `https://api.pyai.com/v1`
- Send the key as a bearer token: `Authorization: Bearer pyai_live_...`
  (`x-api-key: <key>` is an accepted alias).
- Sandbox keys (`pyai_test_...`) are instant, free, and hard-daily-capped - use
  them for prototypes and evals.
- Treat keys as opaque strings (<=512 chars). Never parse, split, decode, or log
  them. They work on every surface the instant they are created.

## Prefer the official SDKs
- TypeScript/JS: `npm install @pyai/sdk` then `import PyAI from "@pyai/sdk"`.
- Python: `pip install pyai-sdk` then `from pyai import PyAI`.
Both wrap auth, retries, idempotency, typed errors, and realtime. Only hand-roll
HTTP if no SDK exists for your language.

## Core endpoints
| Task | Endpoint | Scope |
|------|----------|-------|
| Text-to-speech (Speak) | `POST /v1/audio/speech` | `voice:synthesize` |
| Speech-to-text (Hear) | `POST /v1/audio/transcriptions` | `hear:transcribe` |
| Async batch transcription | `POST/GET /v1/transcription/jobs` | `hear:transcribe` + `transcribe:jobs` |
| Voice / model catalog | `GET /v1/voices`, `GET /v1/models` | any active key |
| All-in-one voice agent (Omni) | `wss://api.pyai.com/v1/omni?format=pcm16&rate=24000` | `omni:session` |

OpenAI-compatible: you can point an OpenAI client at `base_url=https://api.pyai.com/v1`
for transcription and speech and keep your existing code.

## Realtime (Omni): the all-in-one voice agent model
Omni is a hybrid speech-to-speech model with a fused LLM brain: one socket hears
the caller, reasons, calls your tools, grounds answers in your knowledge base, and
speaks back in emotion-aware voices, no STT-LLM-TTS pipeline to assemble.

- Native: `wss://api.pyai.com/v1/omni?format=pcm16&rate=24000`.
- OpenAI-realtime-compatible alias: `wss://api.pyai.com/v1/realtime?model=pyai-omni-realtime`.
- Authenticate on the upgrade with the subprotocol `pyai-key.<key>` (works in the
  browser), or append `?api_key=<key>` for server-side clients.
- Omni is **zero-state**: the session is authorized by your key's org, nothing to
  create first. Set the whole agent with one `configure` frame after connect:
  `voice_id`, `persona`, `kb_endpoint`/`kb_token` (grounding), and `tools[]`
  (function calling). Enable prebuilt **hosted tools** by name with zero setup
  (`search_knowledge`, `web_search`, `weather`, `currency`, `unit_convert`, `math`,
  `datetime`, `geocode`, `news`), or add your own as a signed webhook. Built-in
  call control (`transfer_to_human`, `send_dtmf`, `play_hold`, `collect`,
  `end_call`) is in the engine. `session_label` is an optional opaque tag
  (deprecated alias: `agent_id`).

## Errors - branch on the stable `code`
Data-plane errors use the OpenAI envelope: `{ "error": { "code", "message", "type" } }`.
React to: `402 credit_exhausted` (add credit or use a test key - not a bug),
`429 rate_limit_exceeded` / `concurrency_limit_exceeded` (back off, honor
`Retry-After`), `403 forbidden` (key lacks the scope). Don't retry 4xx except 429.

## Don'ts
- Don't parse or persist key internals; read keys from env vars.
- Don't hardcode deprecated endpoints - fetch the OpenAPI spec.
- Don't assume one error shape; handle the documented codes.

Get a free key at https://console.pyai.com and read the full docs at
https://docs.pyai.com.
