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

# REST API

> Create, configure, version and deploy agents over HTTP.

| API | Base URL |
| - | - |
| Agents | `https://api.zeroruntime.ai/v1/agents` |
| Knowledge bases | `https://api.zeroruntime.ai/v1/knowledge-bases` |
| Phone numbers | `https://api.zeroruntime.ai/v2/sip/phone-numbers` |
| Routing rules | `https://api.zeroruntime.ai/v2/sip/routing-rule` |
| Calls | `https://api.zeroruntime.ai/v2/sip/call` |
| Batch calls | `https://api.zeroruntime.ai/v1/batch-calls` |

## Authentication

Every request carries your API token in the `Authorization` header. Send the token as-is, **without** a `Bearer` prefix.

```bash theme={null}
curl https://api.zeroruntime.ai/v1/agents \
  -H "Authorization: <your-token>"
```

The token identifies the account that owns the agents, and every read and write is scoped to it. An agent id from another account returns `404`, never someone else's data.

## Agents, versions and deployments

| Thing | What it is |
| - | - |
| Agent | The durable identity: `agentId`, name, status. It outlives every config change. |
| Version | One immutable snapshot of the whole config. Every write creates a new one; nothing is edited in place. |
| Deployment | The running agent. It serves whichever version was live when you last deployed. |

<Warning>
  **Editing is not deploying.** Creating an agent deploys its first version straight away. After that, updating and activating only change the latest version. The live agent keeps serving the old config until you call [`POST /v1/agents/{agentId}/deploy`](/api-reference/deployment/deploy-agent).
</Warning>

History is append-only. Activating an older version doesn't rewind anything: it copies that version's content into a new version on top, so the current config is always the newest one and nothing is lost.

## Naming things

Catalog references are written by name and read back by name. Internal ids are accepted too, so you can edit a response and send it straight back. Copy model slugs, voice ids and language codes from [Supported models](/api-reference/rest/models).

| Field | You write | Example |
| - | - | - |
| `model` | `provider/modelId` | `deepgram/nova-3-general` |
| `voice` | The provider's own voice id | `faf0731e-dfb9-…` |
| `language` | Optional. A language code, to pick a regional variant | `en-IN` |
| language key | A language name, any casing | `english`, `hindi` |
| `apiKey` | Your provider's own API key, as-is | `sk-proj-…` |

Language names are matched case-insensitively; responses always come back lowercase.

**Inference or your own key.** Leave `apiKey` off to run a model on our keys (inference), or add your provider's key to the entry to run it on your own account (BYOK). [Supported models](/api-reference/rest/models) shows which of the two each model supports.

```json theme={null}
"llm": [{ "model": "openai/gpt-4o", "apiKey": "sk-proj-…" }]
```

* **Read back masked.** Responses never return a key in full. It comes back masked, with only the last four characters showing: `"apiKey": "••••••••••••AbCd"`.
* **Send the full key every time.** On create and on update, every BYOK entry needs its full `apiKey`. A masked value is rejected with `INVALID_TYPE`, so after a GET, put the full keys back before you send the config.
* **Changing a key** doesn't delete the old one, so older versions still run.

An entry's `language` is optional. Send a code (`en-IN`, any casing) to pick that variant, or leave it out and the plain code for that language (`en`, not `en-IN`) is picked for you. A code the model doesn't offer returns `UNKNOWN_LANGUAGE_DOC`. It is never swapped for a different variant. Each model's codes are listed in [Supported models](/api-reference/rest/models).

## The config object

Config splits on one question: does this vary by language?

| Block | Holds |
| - | - |
| `languages.<name>` | `prompt`, `greeting`, `stt` / `llm` / `tts` (or `realtime`), `turnDetection`, `wakeUp`, `endCall` |
| `voiceProcessing` | `vad`, `interruption`, `noiseCancellation`. Shared by every language. |
| top level | `name`, `defaultLanguage`, `knowledgeBaseIds`, `callSummary`, `tools`, `advanced` |

The model slots (`stt`, `llm`, `tts`, `realtime`) are **arrays**: the first entry is primary, and the rest are fallbacks tried in order. A realtime model goes under `realtime` instead of `stt` / `llm` / `tts`, because it fills all three roles itself. When a block has `realtime`, any `stt`, `llm` or `tts` in the same block is ignored and not stored.

| Field | Accepts |
| - | - |
| `voiceProcessing.interruption.mode` | `hybrid`, `vad` or `stt`. `vad` needs a `voiceProcessing.vad` block. |
| `voiceProcessing.interruption.minDuration` · `minWords` | 0 or more; `minWords` is a whole number. |
| `voiceProcessing.vad.sensitivity` | Required when `vad` is set: `low`, `medium` or `high`, any casing. |
| `voiceProcessing.vad.threshold` | Optional, 0–1. Left out, the platform default applies. |
| `voiceProcessing.vad.minSpeechDuration` · `minSilenceDuration` | Optional, 0 or more. |
| `voiceProcessing.noiseCancellation` | `{ "model": "provider/modelId" }` or `{ "provider": "…" }`. |
| `languages.<name>.turnDetection.mode` | `default` or `adaptive`. |
| `turnDetection.threshold` · `minWaitSeconds` · `maxWaitSeconds` | Threshold 0–1; waits 0 or more, and `maxWaitSeconds` at least `minWaitSeconds`. |
| `languages.<name>.wakeUp` | `enabled`; when `true`, `afterSeconds` (0 or more) and `message` are required. |

<Note>
  **Three fields must agree across languages.** `greeting.interruptible`, `endCall.enabled` and `endCall.when` are stored once for the whole agent. Setting them differently per language returns `MUST_MATCH_ACROSS_LANGUAGES` instead of silently picking one. `endCall.message` is per-language.
</Note>

## Knowledge bases

Give your agent something to know. Upload documents, list website pages or send text, and each one becomes a knowledge base your agent retrieves from during a call. Attach it to an agent by putting its `knowledgeId` in the agent's `knowledgeBaseIds`.

* **One create endpoint.** [`POST /v1/knowledge-bases`](/api-reference/knowledge-bases/create-knowledge-base) takes files as `multipart/form-data`, or website pages or text as JSON.
* **Website pages** must all be on one domain. Exactly the pages you list are parsed. Use [Discover pages](/api-reference/knowledge-bases/discover-pages) to find them.
* **Processing runs in the background.** Poll [Get knowledge base status](/api-reference/knowledge-bases/get-knowledge-base-status) until it's `ready`.
* **Edit the content** with [Edit knowledge base content](/api-reference/knowledge-bases/edit-knowledge-base-content). Each edit is a new version, selected automatically once it finishes indexing.
* **Versions.** A website's auto-refresh adds a new version on every run. Agents read the current version, and you can roll back with [Set current version](/api-reference/knowledge-bases/set-current-version).
* **Folders** group knowledge bases, up to 5 levels deep. Deleting a folder keeps the knowledge bases inside it.

```bash theme={null}
curl -X POST https://api.zeroruntime.ai/v1/knowledge-bases \
  -H "Authorization: <your-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Help center: billing",
    "source": {
      "type": "url",
      "config": {
        "pages": [
          "https://help.example.com/billing",
          "https://help.example.com/plans"
        ]
      }
    }
  }'
```

## Telephony

Bring your own SIP trunk: register the numbers you already own with a SIP address, and we wire an inbound and an outbound gateway for them. Then attach routing rules to decide which agent each call reaches.

* `sipRegion` is one of `auto`, `us002`, `in002`. `auto` stores no region and lets us pick.
* `mediaEncryption` is `enable` or `disable` (default `disable`).
* `transport` is `udp`, `tcp` or `tls` (default `tls`).
* Numbers are always E.164, with the `+`.

<Warning>
  The phone-number endpoints never return `5xx`. An unexpected server fault comes back as `400` with `{"message": "Internal Server Error!! "}`, so don't branch on the status class alone. Routing rules use `500` for the same case.
</Warning>

A number can carry one **inbound** routing rule and many **outbound** ones. `headersToAttributes` is inbound-only and `headers` is outbound-only; putting either on the wrong type returns `400`.

## Batch calls

Upload a spreadsheet of phone numbers and your agent calls every one of them, right away or on a schedule. One request does it all: [`POST /v1/batch-calls`](/api-reference/batch-calls/create-batch-call) takes the file and the settings together as `multipart/form-data`, and the batch starts by itself once the file is parsed.

* **Before you start**, you need a number with an outbound gateway, and an outbound routing rule on it that points at your agent. Pass the number as `phoneNumber` (E.164).
* **The file** is `.csv`, `.xls` or `.xlsx`, at most 5 MB, with a `phone_number` column. Every other column reaches the agent as metadata for that call.
* **`schedule`, `retry` and `webhook`** are JSON strings inside the form. Leave `schedule` out to start calling as soon as parsing finishes.
* **Follow progress** with [Get batch call](/api-reference/batch-calls/get-batch-call): `parsing` → `scheduled` or `running` → `completed`. A file that can't be used ends in `parse_failed`, with the reason in `parseError`.

```bash theme={null}
curl -X POST https://api.zeroruntime.ai/v1/batch-calls \
  -H "Authorization: <your-token>" \
  -F "file=@customers.csv" \
  -F "name=October renewals" \
  -F "phoneNumber=+14155550100"
```

## Agent sessions

See what happened on every call: what the user and the agent said, how long each stage took, and what it used. [`GET /v1/agents/sessions`](/api-reference/agents/list-agent-sessions) returns your sessions newest first, each with its turns. Pass a `roomId`, an `agentId`, or both to narrow it down.

* **One turn is one exchange.** `user` and `agent` carry the transcript and speech times in Unix epoch milliseconds.
* **Each stage is its own block**: `stt`, `llm`, `tts` (or `realtime` for speech-to-speech models), `eou` and `denoise`, with the provider, model, latency and usage.
* **A stage that didn't run is `null`.** The greeting has no `stt` or `llm`, and a turn where the user was cut off may have no `llm` or `tts`.
* **The agent's transcript is what it generated.** If the user interrupted, it can be longer than what was actually heard; `interrupted` tells you when.

```bash theme={null}
curl "https://api.zeroruntime.ai/v1/agents/sessions?roomId=kq1v-wa7n-p75u" \
  -H "Authorization: <your-token>"
```


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