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

# Create agents via API

> Use the Agents REST API to manage the same profiles as the console, then run voice sessions on the Omni realtime runtime.

An **agent profile** stores persona, voice, greeting, and recording consent so you do not send a full `configure` frame on every call. Open Omni with `session_label={agent_id}`. Inline `configure` fields still win for that session.

**Agents** is one product with UI and API access. The console and REST API write
the same saved profile; **Omni** runs its realtime calls. You can
[build in the console](/agents/getting-started), use code, or switch between them.

Use a key from the project you have selected in the console. Agents created with
that key appear under **Your agents** in that project. Instant sandbox keys
create separate sandbox projects; they do not automatically belong to an
existing console project.

## Which API to use

| Task | Interface |
| - | - |
| Create, list, read or update a saved agent | Agents REST API under `/v1/agents` |
| Bind hosted knowledge or registered tools | `/v1/agents/{id}/knowledgebases` and `/v1/agents/{id}/tools` |
| Run a voice conversation | Omni WebSocket at `/v1/omni`, selecting the saved agent with `session_label` |
| Configure a call without a saved profile | The same Omni WebSocket with an inline `configure` frame |

The required agent and runtime scope is `omni:session`. Additional knowledge
management and tool-registration operations have their own scopes. The existing
endpoint names are retained for compatibility; no client migration is required
for the unified Agents workspace.

## Create the profile

Scope: `omni:session`.

```bash theme={null}
curl -X POST https://api.pyai.com/v1/agents \
  -H "Authorization: Bearer $PYAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Front desk",
    "role": "receptionist",
    "persona_system_prompt": "You are the after-hours receptionist for North Clinic. Answer from the bound knowledge base. Transfer when the caller asks for a human.",
    "greeting": "Thanks for calling North Clinic. How can I help?",
    "voice_id": "stock_aria_en",
    "voice_instruct": "Speak at a brisk, natural conversational pace. Keep pauses short.",
    "vocabulary": ["North Clinic", "Nguyen", "CardioFlex"],
    "recordings_enabled": true,
    "consent_line": "This call may be recorded for quality and training."
  }'
```

The response includes `agent_id` (`agent_...`). List and read the same objects
at `GET /v1/agents` and `GET /v1/agents/{id}`. Partial updates use
`POST /v1/agents/{id}`; present fields are changed, `null` clears a field, and
omitted fields remain unchanged. `GET /v1/agents` lists active profiles across
the key's organization; the console list is filtered to the selected project.
Open the saved agent's **API integration** tab to get examples with its ID.

`role` activates PyAI's role operating standard beneath your persona; your
persona remains authoritative for identity and business policy.
`voice_instruct` controls delivery on instruct-capable voice tiers. If omitted
or reset to `null`, managed Agents use PyAI's natural conversational pace.

## Add custom vocabulary

`vocabulary` is an optional Agent-owned list for distinctive names, brands,
products, or short phrases. A non-empty list is the opt-in. PyAI sanitizes the
list with the same rules used by Hear and keeps at most five effective terms.
Set `vocabulary` to `[]` or `null` to turn it off.

The sanitized list is fixed when a new Omni session starts and remains fixed
across a transport reconnect. Organization vocabulary from
`/v1/hear/vocabulary` is never applied to Omni. There is no inline Omni
vocabulary override in this release. Update the Agent before starting a new
session when the list needs to change.

Use a short, specific list. Custom vocabulary can improve rendering of known
entities while making unrelated words worse. See
[Format Hear transcripts](/guides/hear-transcript-formatting) for the measured
curated-evaluation result and its word-error-rate tradeoff.

## Bind a hosted knowledge base

Scope: `kb:manage` to create the base and documents, `omni:session` to bind.

```bash theme={null}
KB_ID="$(
  curl -sS -X POST https://api.pyai.com/v1/knowledgebases \
    -H "Authorization: Bearer $PYAI_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"name": "Clinic site"}' \
    | python -c 'import json,sys; print(json.load(sys.stdin)["id"])'
)"

curl -X POST "https://api.pyai.com/v1/knowledgebases/$KB_ID/crawls" \
  -H "Authorization: Bearer $PYAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.example.com", "max_pages": 25}'

curl -X PUT "https://api.pyai.com/v1/agents/agent_.../knowledgebases" \
  -H "Authorization: Bearer $PYAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d "[{\"kb_id\": \"$KB_ID\", \"weight\": 1}]"
```

Poll documents until `status` is `indexed` before you treat the site as ready. See [Knowledge bases](/guides/knowledge-bases). If you already run retrieval, keep `kb_endpoint` on the Omni `configure` frame instead.

Sandbox keys from `POST /v1/sandbox/keys` omit `kb:manage`. Create a console key when you need hosted KB writes.

## Bind tools

Register a server tool with `POST /v1/tools`, then `PUT /v1/agents/{id}/tools`. Hosted catalog tools bind by name (`datetime`, `web_search`, and the rest). Do not put `endpoint` or `webhook_url` on the Omni `configure` frame. That is rejected as `unsupported_tool_transport`.

Details: [Omni tools](/guides/omni-tools). Greeting and consent: [Agent greetings](/guides/agent-greeting).

## Open a session

```
wss://api.pyai.com/v1/omni?session_label=agent_...&format=pcm16&rate=24000
```

`session_label` is an opaque tag. When it matches an agent id, Omni loads that profile. The same label is echoed to a customer `kb_endpoint` if you use one.

Auth: `Sec-WebSocket-Protocol: pyai.v1, pyai-key.$PYAI_API_KEY` in browsers, or `Authorization: Bearer` on the server. Frame contract: [Omni wire protocol](/realtime/omni-protocol).

## Website or phone

The hosted Call Now button is a console publish: [Add your Agent to a website](/guides/website-voice-widget). Phone numbers stay on [Twilio](/guides/twilio-voice-agent) or managed telephony in the console.

## Next

<CardGroup cols={2}>
  <Card title="Launch an Agent" href="/agents/getting-started">Same profile, console workflow.</Card>
  <Card title="Knowledge bases" href="/guides/knowledge-bases">Hosted files, URLs, and pasted text.</Card>
  <Card title="Omni overview" href="/guides/omni-overview">Connect without a stored profile.</Card>
  <Card title="API reference" href="/api-reference">`/v1/agents` schemas.</Card>
</CardGroup>


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