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

# voice agents

> run a conversational agent from your own app — in the browser, over a socket, or on the phone.

a voice agent is an agent you configure once — its prompt, greeting, voice
and language — in the [dashboard](https://playground.rumik.ai) or with the
[agents api](/agents-api), and then start from your own product with a single
api call. the agent listens, thinks and speaks; you only carry audio, or let
the phone network carry it.

there are three ways to connect a caller, and the right one depends on where
the audio lives.

<CardGroup cols={3}>
  <Card title="web call" icon="browser" href="/web-call">
    your **browser** joins the call directly over webrtc. lowest latency, and the
    media never passes through your servers.
  </Card>

  <Card title="realtime socket" icon="waveform-lines" href="/realtime-agent">
    a plain **websocket** carrying base64 pcm. no webrtc stack — good for
    servers, native apps and anything that already has audio buffers.
  </Card>

  <Card title="phone call" icon="phone" href="/outbound-calls">
    the agent **dials a number** and talks to whoever answers. no audio on your
    side at all; inbound calls on your numbers land on the agent too.
  </Card>
</CardGroup>

## which one

|                   | web call             | realtime socket                    | phone call                      |
| ----------------- | -------------------- | ---------------------------------- | ------------------------------- |
| transport         | webrtc (livekit)     | websocket                          | pstn                            |
| audio handled by  | the caller's browser | you                                | the phone network               |
| client dependency | a livekit client SDK | none                               | none                            |
| audio format      | negotiated for you   | pcm s16le mono 24 kHz              | n/a                             |
| best for          | web apps             | servers, mobile, telephony bridges | reaching people on their phones |

the choice of transport does not affect what a call costs — see
[billing](#billing) for that, which depends on your plan rather than on how the
caller connected.

## authentication

every endpoint takes your api key as a bearer token:

```
Authorization: Bearer rk_live_•••••••••
```

the key needs the **`agent` scope**. keys created in the dashboard have it by
default; older keys were granted it automatically, so nothing you already ship
stops working.

<Warning>
  never put a `rk_live_` key in a browser. for web calls, start the call from
  your server and pass the returned join credentials to the page. for the
  realtime socket, mint a short-lived token with
  [`/v1/register-call`](/register-call) and hand *that* to the page.
</Warning>

## before you start

<Steps>
  <Step title="create an agent">
    in the dashboard, open **agents** and build one — or
    [`POST /v1/agents`](/manage-agents). note its id (a UUID) or its handle
    (`ua_…`) — either works everywhere an agent is named.
  </Step>

  <Step title="deploy it">
    press **deploy**, or [`POST /v1/agents/{agent_ref}/deploy`](/manage-agents#deploy).
    saving records a draft; deploying is what puts a version live. the api
    refuses an agent that has never been deployed with `409 agent_not_deployed`,
    so it can never answer a caller with a configuration you did not release.
  </Step>

  <Step title="create an api key">
    **api keys** → new key. the full key is shown once.
  </Step>

  <Step title="check your capacity">
    [`GET /v1/agent/limits`](/agent-limits) tells you how many concurrent calls
    your plan allows and how many are running right now.
  </Step>
</Steps>

## errors

every endpoint — these and the [agents api](/agents-api#errors) — answers with
the same envelope the rest of the api uses:

```json theme={null}
{ "error": "human-readable message", "code": "machine_readable_code" }
```

| status | `code`                       | what happened                                           |
| ------ | ---------------------------- | ------------------------------------------------------- |
| 401    | `unauthorized`               | key missing, unknown, revoked or expired                |
| 403    | `forbidden_scope`            | the key lacks the `agent` scope                         |
| 404    | `agent_not_found`            | no such agent on your account                           |
| 409    | `agent_not_deployed`         | the agent exists but has never been deployed            |
| 402    | `insufficient_balance`       | your balance can't fund a call                          |
| 402    | `access_blocked`             | account paused after a failed payment                   |
| 429    | `concurrency_limit_exceeded` | all your concurrent slots are busy                      |
| 429    | `rate_limited`               | the key's requests-per-minute budget; see `Retry-After` |
| 422    | `invalid_request`            | a field is missing or malformed                         |
| 502    | `agent_start_failed`         | the agent could not be started — safe to retry          |
| 503    | `not_configured`             | voice agents are unavailable on this deployment         |

a `429` carries the numbers you need to back off intelligently:

```json theme={null}
{
  "error": "Silk is already processing 4 requests for this account. Try again when one finishes, or increase your plan capacity.",
  "code": "concurrency_limit_exceeded",
  "active_requests": 4,
  "limit": 4
}
```

## billing

how a call is paid for depends on which plan the account is on. the two work
differently enough that it is worth knowing which one you are testing against —
`plan` in [`/v1/agent/limits`](/agent-limits) tells you.

<Tabs>
  <Tab title="credits (payg)">
    every second is billed from your balance, at the agent's per-minute rate.

    a call is capped at what your balance can fund: if you can only afford 40
    seconds, the call ends after 40 seconds rather than being refused up front.
    if you cannot afford a usable call at all, the start returns
    `402 insufficient_balance`.

    new accounts also get a small grant of free agent seconds, which is spent
    before your balance is touched.
  </Tab>

  <Tab title="concurrency (unlimited)">
    your plan includes a fixed number of simultaneous calls. calls run on those
    slots and cost **nothing per second** — there is no per-call charge to
    compute and nothing is metered against duration.

    when every slot is busy, the next call is refused with
    `429 concurrency_limit_exceeded`. wait for one to finish, or move to a
    larger plan.
  </Tab>
</Tabs>

### concurrency is used first, and credits are never a fallback

an account can hold a concurrency plan **and** a credit balance at the same
time. when it does:

<Steps>
  <Step title="the plan is used first">
    while a slot is free, the call runs on the plan. your credit balance is not
    touched — not partially, not for the overflow, not at all.
  </Step>

  <Step title="when the slots run out, the call is refused">
    a call that arrives with every slot busy gets `429 concurrency_limit_exceeded`.
    it does **not** fall through to your credits, even if the balance would
    comfortably cover it.
  </Step>
</Steps>

<Warning>
  credits are not an overflow buffer for a concurrency plan. if you are on a
  concurrency plan, a balance sitting in the account will never be spent on
  agent calls — the only thing that raises your ceiling is a bigger plan.
</Warning>

so a single account behaves like exactly one of these at a time:

| account is on…                     | a call costs                 | past the concurrency limit                 |
| ---------------------------------- | ---------------------------- | ------------------------------------------ |
| credits, no plan                   | per second, from the balance | `429`, and `402` once the balance runs out |
| a concurrency plan                 | nothing                      | `429` — credits are not used               |
| a concurrency plan **and** credits | nothing                      | `429` — credits are still not used         |

### where calls show up

every call appears in **conversations** in the dashboard with its transcript and
recording, and in your usage — attributed to the api key that started it.
calls that ran on a concurrency plan appear there too, at zero cost.
