> ## 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.

# agents api

> everything the agents playground does, from your own code.

every button in the agents playground has an endpoint behind it, and every one
of them takes your api key. build an agent, pick its voice, declare the
variables its prompt reads, wire up tools, rent a number or bring your own
over SIP, deploy, and put it on the phone — without opening the dashboard.

<CardGroup cols={2}>
  <Card title="manage agents" icon="robot" href="/manage-agents">
    create, configure, deploy, roll back, delete.
  </Card>

  <Card title="voices and languages" icon="waveform" href="/voices">
    what `ttsConfig` and `language` may be set to.
  </Card>

  <Card title="variables and tools" icon="brackets-curly" href="/variables-and-tools">
    `{name}` placeholders with defaults, filled by your own endpoints.
  </Card>

  <Card title="outbound calls" icon="phone-arrow-up-right" href="/outbound-calls">
    dial a number and connect the caller to an agent.
  </Card>

  <Card title="phone numbers" icon="hashtag" href="/phone-numbers">
    rent a number and connect it to an agent for inbound calls.
  </Card>

  <Card title="sip trunks" icon="tower-cell" href="/sip-trunks">
    bring your own carrier numbers.
  </Card>
</CardGroup>

## base url and authentication

the same base URL and key as the rest of the api:

```
https://silk-api.rumik.ai
Authorization: Bearer rk_live_•••••••••
```

the key needs the **`agent` scope** — keys created in the dashboard have it by
default. a key without it gets `403 forbidden_scope`.

<Warning>
  these endpoints change your account: they create agents, rent numbers that
  cost money, and place calls that are billed. keep the key on your server.
</Warning>

## naming an agent

everywhere an agent is named — in a path, in `agentId` — you can pass either
its UUID or its short handle (`ua_…`). both come back on every agent object.

```
GET /v1/agents/019f6a2e-7c1d-7b3a-9e4f-1a2b3c4d5e6f
GET /v1/agents/ua_fe3277d8
```

## draft, then deploy

saving an agent never changes what it says on a live call. `POST` and `PATCH`
write a **draft**; `POST /v1/agents/{agent_ref}/deploy` promotes it to a
numbered version and puts it live. an agent that has never been deployed
(`deployed: false`) answers no calls and cannot be connected to a number.

```mermaid theme={null}
flowchart LR
  create["POST /v1/agents"] --> draft["draft (v1)"]
  draft --> deploy["POST …/deploy"]
  deploy --> live["active v1"]
  live --> edit["PATCH /v1/agents/{ref}"]
  edit --> draft2["draft (v2)"]
  draft2 --> deploy2["POST …/deploy"]
  deploy2 --> live2["active v2"]
```

every deploy is kept: `GET /v1/agents/{agent_ref}/versions` lists them and
`POST …/versions/{n}/activate` rolls back.

## field names

bodies and responses are camelCase (`systemInstruction`, `toNumber`). the
snake\_case spelling is accepted on input too. ids are UUIDs, timestamps are
ISO 8601 in UTC, money is in **nanos** — billionths of the wallet's currency
unit, so `236250000000` nanos INR is ₹236.25.

## errors

the same envelope as every other endpoint:

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

| status | `code`                                                                     | what happened                                             |
| ------ | -------------------------------------------------------------------------- | --------------------------------------------------------- |
| 401    | `unauthorized`                                                             | key missing, malformed, unknown, revoked or expired       |
| 403    | `forbidden_scope`                                                          | the key lacks the `agent` scope                           |
| 404    | `agent_not_found` and friends                                              | the thing does not exist on your account                  |
| 409    | `agent_not_deployed`, `agent_version_no_draft`, `…_in_use`, `…_name_taken` | the request contradicts the current state                 |
| 422    | `invalid_request`                                                          | a field is missing or malformed; `details` lists each one |
| 429    | `rate_limited`                                                             | the key's per-minute budget; wait `Retry-After` seconds   |
| 429    | `concurrency_limit_exceeded`                                               | every concurrent call slot is busy                        |
| 502    | `agent_session_failed`, `sip_trunk_provision_failed`                       | an upstream refused; safe to retry                        |
| 503    | `…_not_configured`                                                         | the feature is off on this deployment                     |

a `422` carries the failing fields:

```json theme={null}
{
  "error": "body.toNumber: must be an E.164 phone number, e.g. +14155551234",
  "code": "invalid_request",
  "details": [
    { "loc": "body.toNumber", "message": "must be an E.164 phone number, e.g. +14155551234" }
  ]
}
```

each endpoint's page in the [api reference](/api-reference) lists the exact
codes it can answer.

## rate limits

each key has its own budget of requests per minute (100 by default; the
dashboard shows it on the key). a `429 rate_limited` carries `limit` and
`current` and a `Retry-After` header. calls themselves are bounded by your
account's concurrency, not by this — see [limits](/agent-limits).

## from zero to a phone call

<Steps>
  <Step title="create an agent">
    `POST /v1/agents` with a prompt, a greeting and a voice.
  </Step>

  <Step title="deploy it">
    `POST /v1/agents/{agent_ref}/deploy`.
  </Step>

  <Step title="call someone">
    `POST /v1/calls` with the agent and a number. the agent is on the line the
    moment they answer.
  </Step>
</Steps>

<CodeGroup>
  ```bash curl theme={null}
  BASE=https://silk-api.rumik.ai
  AUTH="Authorization: Bearer rk_live_•••••••••"

  # 1. create
  AGENT=$(curl -s -X POST $BASE/v1/agents -H "$AUTH" -H "Content-Type: application/json" -d '{
    "name": "support",
    "systemInstruction": "You are the support agent for acme. Be brief and warm.",
    "greeting": "Hi, this is acme support. How can I help?",
    "ttsConfig": { "model": "mulberry", "voice": "Emma" },
    "language": "english"
  }' | jq -r .handle)

  # 2. deploy
  curl -s -X POST $BASE/v1/agents/$AGENT/deploy -H "$AUTH"

  # 3. call
  curl -s -X POST $BASE/v1/calls -H "$AUTH" -H "Content-Type: application/json" \
    -d "{ \"agentId\": \"$AGENT\", \"toNumber\": \"+14155551234\" }"
  ```

  ```python python theme={null}
  import requests

  API_KEY = "rk_live_•••••••••"
  BASE = "https://silk-api.rumik.ai"
  HEADERS = {"Authorization": f"Bearer {API_KEY}"}

  agent = requests.post(f"{BASE}/v1/agents", headers=HEADERS, json={
      "name": "support",
      "systemInstruction": "You are the support agent for acme. Be brief and warm.",
      "greeting": "Hi, this is acme support. How can I help?",
      "ttsConfig": {"model": "mulberry", "voice": "Emma"},
      "language": "english",
  }).json()

  requests.post(f"{BASE}/v1/agents/{agent['handle']}/deploy", headers=HEADERS).raise_for_status()

  call = requests.post(f"{BASE}/v1/calls", headers=HEADERS, json={
      "agentId": agent["handle"],
      "toNumber": "+14155551234",
  }).json()
  print(call["callId"], call["status"])   # "…", "calling"
  ```

  ```javascript node theme={null}
  const BASE = "https://silk-api.rumik.ai";
  const headers = {
    Authorization: `Bearer ${process.env.RUMIK_API_KEY}`,
    "Content-Type": "application/json",
  };

  const agent = await fetch(`${BASE}/v1/agents`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      name: "support",
      systemInstruction: "You are the support agent for acme. Be brief and warm.",
      greeting: "Hi, this is acme support. How can I help?",
      ttsConfig: { model: "mulberry", voice: "Emma" },
      language: "english",
    }),
  }).then((r) => r.json());

  await fetch(`${BASE}/v1/agents/${agent.handle}/deploy`, { method: "POST", headers });

  const call = await fetch(`${BASE}/v1/calls`, {
    method: "POST",
    headers,
    body: JSON.stringify({ agentId: agent.handle, toNumber: "+14155551234" }),
  }).then((r) => r.json());
  console.log(call.callId, call.status);   // "…", "calling"
  ```
</CodeGroup>

next: [manage agents](/manage-agents).
