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

# Assistants

> Create and manage the AI voice agents that answer and place your calls — the assistant object, its configuration document, and how the endpoints fit together.

An **assistant** is one AI voice agent: a system prompt, a voice, the tools it may call, and the settings that govern a call. Everything a phone number or a campaign dials is an assistant.

## Endpoints

| Endpoint | Purpose |
| - | - |
| [`POST /assistant`](/api-reference/assistants/create) | Create an assistant |
| [`POST /assistant/list`](/api-reference/assistants/list) | List your assistants, paged and searchable |
| [`GET /assistant/{id}`](/api-reference/assistants/get) | One assistant, with its complete configuration |
| [`PATCH /assistant/{id}`](/api-reference/assistants/update) | Change the name, type or any part of the configuration |
| [`PUT /assistant/{id}`](/api-reference/assistants/replace) | Replace the whole configuration |
| [`DELETE /assistant/{id}`](/api-reference/assistants/delete) | Delete an assistant |

The workspace comes from your API key, so no endpoint asks you for a workspace id.

<Note>
  Listing is a `POST` because a list query is a document — page, size and a search term, typed as themselves rather than coerced from a query string. It reads, and changes nothing.
</Note>

## The assistant object

```json theme={null}
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "workspace_id": "123e4567-e89b-12d3-a456-426614174000",
  "agent_name": "Customer Support",
  "agent_type": "Single Prompt",
  "config": { "…": "see below" },
  "sip_uri": "sip:a1b2c3@sip.verbeo.ai",
  "has_cal_integration": false,
  "created_at": "2026-01-15T10:30:00.000Z",
  "updated_at": "2026-01-22T14:00:00.000Z"
}
```

<ResponseField name="id" type="string" required>UUID of the assistant.</ResponseField>
<ResponseField name="agent_name" type="string" required>Display name, 1–255 characters.</ResponseField>
<ResponseField name="agent_type" type="string" required>Prompt style. `"Single Prompt"` is the only supported value today.</ResponseField>
<ResponseField name="config" type="object" required>The configuration document — see below.</ResponseField>
<ResponseField name="sip_uri" type="string">The assistant's own inbound SIP URI, when SIP is provisioned. Point a carrier at it to route calls here.</ResponseField>
<ResponseField name="created_at" type="string" required>ISO 8601 timestamp.</ResponseField>
<ResponseField name="updated_at" type="string" required>ISO 8601 timestamp of the last change.</ResponseField>

## The configuration document

`config` is where the assistant's behaviour lives. Send only the parts you care about — anything you leave out is filled in with the same defaults the dashboard gives a new assistant, and the completed document is what comes back. To change it later, [`PATCH`](/api-reference/assistants/update) merges what you send into it, and [`PUT`](/api-reference/assistants/replace) replaces it.

```json theme={null}
{
  "systemPrompt": "You are a friendly support agent for Acme…",
  "voice": { "voiceId": "Charon" },
  "toolIds": ["4f1c…"],
  "fileSearchStoreIds": [],
  "nativeTools": {
    "end_call": { "enabled": true },
    "transfer_call": {
      "enabled": true,
      "mode": "static",
      "staticDestination": "+15551234567"
    }
  },
  "settings": {
    "initialMessage": { "enabled": true },
    "maxCallDuration": 10,
    "endCallOnSilence": true,
    "silenceTimeoutMinutes": 2,
    "voicemailDetection": { "enabled": true, "action": "hangup" },
    "thinkingLevel": "medium",
    "webhookUrl": "",
    "postCallAnalysis": { "enabled": false, "fields": [] }
  }
}
```

<ResponseField name="systemPrompt" type="string">
  The instructions the assistant follows. Supports `{{variable}}` placeholders filled in per call.
</ResponseField>

<ResponseField name="llm.model" type="string" default="gemini-3.8-live">
  Every assistant runs on Gemini 3.8 Live, so this is always stored as `gemini-3.8-live`, whatever a request sends.
</ResponseField>

<ResponseField name="voice.voiceId" type="string" default="Charon">
  One of: `Zephyr`, `Puck`, `Charon`, `Kore`, `Fenrir`, `Leda`, `Orus`, `Aoede`, `Callirrhoe`, `Autonoe`, `Enceladus`, `Iapetus`, `Umbriel`, `Algieba`, `Despina`, `Erinome`, `Algenib`, `Rasalgethi`, `Laomedeia`, `Achernar`, `Alnilam`, `Schedar`, `Gacrux`, `Pulcherrima`, `Achird`, `Zubenelgenubi`, `Vindemiatrix`, `Sadachbia`, `Sadaltager`, `Sulafat`. Anything else is rejected with `VALIDATION_FAILED` rather than quietly substituted — a voice you did not choose is an assistant that does not sound like the one you asked for.
</ResponseField>

<ResponseField name="toolIds" type="string[]">
  Tools the assistant may call. Ids that do not belong to your workspace are dropped, and when two ids resolve to the same tool name only one is kept — a duplicate declaration is refused by the model at connect time, which would leave the call live and silent. The [Tools](/api-reference/tools/overview) endpoints add a tool's id here when they create it and take it out when they delete it.
</ResponseField>

<ResponseField name="fileSearchStoreIds" type="string[]">
  Knowledge bases the assistant may search. Each must belong to your workspace.
</ResponseField>

<ResponseField name="settings.maxCallDuration" type="number" default="10">
  Hard ceiling on a call, **in minutes**: at most **30**. A larger value is saved as 30.
</ResponseField>

<ResponseField name="settings.silenceTimeoutMinutes" type="number" default="2">
  How long a caller may stay silent before the assistant ends the call, in minutes. Only applies when `endCallOnSilence` is `true`.
</ResponseField>

<ResponseField name="settings.voicemailDetection" type="object | boolean" default="{ enabled: true, action: 'hangup' }">
  Accepts `true`/`false` or `{ "enabled": true, "action": "hangup" }`.
</ResponseField>

<ResponseField name="settings.thinkingLevel" type="string" default="medium">
  Kept for compatibility, with no effect: every assistant runs on Gemini 3.8 Live, which always runs at `minimal`, whatever is stored here.
</ResponseField>

<ResponseField name="settings.webhookUrl" type="string">
  Where call events for this assistant are delivered. See [Webhook events](/api-reference/webhooks/events).
</ResponseField>

<ResponseField name="settings.postCallAnalysis" type="object">
  Structured fields to extract from each transcript. See [Post-call analysis](/guides/post-call-analysis).
</ResponseField>

## Native tools

`nativeTools` switches on the behaviour Verbeo implements for you. Each entry is **three-state**:

| What you send | What happens |
| - | - |
| the key is absent | left exactly as it is |
| `{ "enabled": true, … }` | switched on, and provisioned if it does not exist yet |
| `{ "enabled": false }` | switched off; its settings are remembered for when you turn it back on |

<ResponseField name="nativeTools.end_call" type="object">
  Lets the assistant hang up when the conversation is over. On by default for a new assistant — without it the assistant stays on the line, billing, after the caller has said goodbye.
</ResponseField>

<ResponseField name="nativeTools.transfer_call" type="object">
  Hands the call to a human. `mode: "static"` always dials `staticDestination`; `mode: "dynamic"` lets the assistant choose a number, guided by `dynamicPrompt`. Also takes `transferType` (`cold` or `warm`).
</ResponseField>

<ResponseField name="nativeTools.send_sms" type="object">
  Sends a text message during the call. `smsContentType: "static"` sends `smsStaticMessage` verbatim; `"prompt"` lets the assistant compose it.
</ResponseField>

Fields you do not send are left unchanged on a tool that already exists, so you can retarget a transfer without restating the rest of it.

The [native tool endpoints](/api-reference/tools/create-native) change the same tools, under the tool's own field names (`transfer_call_destination` for `staticDestination`), and also set what `nativeTools` cannot: a `send_sms` tool's Twilio account and its timeout.

## Authentication

Every endpoint takes a **private** workspace API key (`vbr_…`) as a Bearer token. Public widget keys (`vbr_pub_…`) are rejected with `403 FORBIDDEN` — they ship inside web pages, where anyone can read them. See [Authentication](/api-reference/authentication).


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