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

# Migrate from v2 to v3

> What changes when you move your AI agent integration from /v2/ai-agents to /v3/ai-agents.

The AI Agents v2 create, list, get, update and delete operations are deprecated.

| Date | What happens |
| - | - |
| October 17, 2026 | v2 agent CRUD is deprecated. It keeps working, unchanged. |
| November 30, 2026 | v2 agent CRUD stops working. Requests after this date can fail. |

Move to v3 before November 30, 2026. This page lists every difference you have to handle.

## Endpoint map

| v2 (deprecated) | v3 |
| - | - |
| [`POST /v2/ai-agents/`](https://docs.verbex.ai/api-reference/ai-agents-v2/create-ai-agent) | [`POST /v3/ai-agents`](https://docs.verbex.ai/api-reference/ai-agents-v3/create-ai-agent) |
| [`GET /v2/ai-agents/`](https://docs.verbex.ai/api-reference/ai-agents-v2/list-ai-agents) | [`GET /v3/ai-agents`](https://docs.verbex.ai/api-reference/ai-agents-v3/list-ai-agents) |
| [`GET /v2/ai-agents/{agent_id}`](https://docs.verbex.ai/api-reference/ai-agents-v2/get-ai-agent) | [`GET /v3/ai-agents/{agent_id}`](https://docs.verbex.ai/api-reference/ai-agents-v3/get-ai-agent) |
| [`PUT /v2/ai-agents/{agent_id}`](https://docs.verbex.ai/api-reference/ai-agents-v2/update-ai-agent) | [`PUT /v3/ai-agents/{agent_id}`](https://docs.verbex.ai/api-reference/ai-agents-v3/update-ai-agent) |
| [`DELETE /v2/ai-agents/{agent_id}`](https://docs.verbex.ai/api-reference/ai-agents-v2/delete-ai-agent) | [`DELETE /v3/ai-agents/{agent_id}`](https://docs.verbex.ai/api-reference/ai-agents-v3/delete-ai-agent) |

Authentication is unchanged: the same `Authorization: Bearer <API_KEY>` header, the same tenant scoping and the same base URL.

## What is not deprecated

Only the five agent CRUD operations above are deprecated. These operations keep their `/v2/ai-agents/{ai_agent_id}/…` paths and keep working:

* [Built-in tools](https://docs.verbex.ai/api-reference/built-in-tools/list-built-in-tools.md)
* [Custom tools](https://docs.verbex.ai/api-reference/custom-tools/list-custom-tools.md)
* [Post-call analysis](https://docs.verbex.ai/api-reference/post-call-analysis/get-post-call-analysis-configuration-for-an-agent.md)

An agent created with v3 has the same `ai_agent_id` you pass to those operations.

## 1. Every response is wrapped

v2 returned the agent at the top level, with `status` and `message` added next to the agent's own fields. v3 wraps the result in a standard envelope: `status`, `message`, `data`, and `error_details` on some errors.

```json v2 theme={null}
{
  "ai_agent_id": "3f6c2a1e-8b4d-4c9a-9e2f-7d1b5a6c0e42",
  "agent_name": "Customer Support Agent",
  "status": "SUCCESS",
  "message": "Simple agent created successfully",
  "operation_type": "CREATE"
}
```

```json v3 theme={null}
{
  "status": "success",
  "message": "Simple agent created successfully",
  "data": {
    "ai_agent_id": "3f6c2a1e-8b4d-4c9a-9e2f-7d1b5a6c0e42",
    "agent_name": "Customer Support Agent",
    "operation_type": "CREATE"
  }
}
```

> [!WARNING]
>
> **`status` is lowercase on v3** (`success` or `error`). v2 returned `SUCCESS` in uppercase. If your code compares `status`, update the comparison.

### The list moves one level down

The agents move from the top-level `data` array to **`data.items`**. The cursor moves from the top-level `next_page_token` to **`data.next_page_token`**.

```json v2 theme={null}
{ "data": [ "…agents…" ], "next_page_token": "…" }
```

```json v3 theme={null}
{
  "status": "success",
  "message": "Agents retrieved successfully",
  "data": { "items": [ "…agents…" ], "next_page_token": "…" }
}
```

`next_page_token` is omitted on the last page in both versions.

### Errors on get now have a body

Errors already used the `{ "status": "error", "message", "error_details" }` shape on both versions, with one exception. `GET /v2/ai-agents/{agent_id}` answered `404` and `403` with an **empty body**. The v3 get answers with the error envelope and a message:

```json theme={null}
{
  "status": "error",
  "message": "Agent not found with ID: 3f6c2a1e-8b4d-4c9a-9e2f-7d1b5a6c0e42"
}
```

If your client skips parsing the body on a `404` or `403`, it keeps working. If you want the message, parse the body.

## 2. Delete returns 200, not 204

`DELETE /v3/ai-agents/{agent_id}` answers `200` with the envelope. `data` holds the agent as it was before the delete. v2 answered `204` with no body. Update client code that treats `204` as the only success code, or that never reads the body.

## 3. LLM, STT and TTS are selected by id

This is the main change. v2 took provider and model **names**. v3 takes **catalog ids**, and the server resolves them.

```json v2 theme={null}
{
  "agent_name": "Customer Support Agent",
  "llm": {
    "llm_type": "simple",
    "model_provider": "openai",
    "model_name": "gpt-4o-mini",
    "system_prompt": "You are a helpful support assistant."
  },
  "stt": { "provider": "deepgram", "model": "nova-2-phonecall" },
  "tts": { "provider": "elevenlabs", "voice_id": "cgSgspJ2msm6clMCkdW9", "model_name": "eleven_turbo_v2_5" }
}
```

```json v3 theme={null}
{
  "agent_name": "Customer Support Agent",
  "llm": {
    "llm_type": "simple",
    "llm_id": "9d259647-d5ec-423d-96a9-2750980e8ecb",
    "system_prompt": "You are a helpful support assistant."
  },
  "stt": { "provider_id": "6a5c45808d2d94e9d0e255d3", "model_id": "aec2493a-eba9-48d0-9e64-4aab4437f7f5" },
  "tts": { "provider_id": "6a5c079f814b8ed08b15172b", "voice_id": "cgSgspJ2msm6clMCkdW9" }
}
```

| v2 fields | v3 replacement | Look up with |
| - | - | - |
| `llm.model_provider`, `llm.model_name`, `llm.base_url`, `llm.api_key`, `llm.max_token` | `llm.llm_id` | [List LLM models](https://docs.verbex.ai/api-reference/llm-models/list-llm-models.md): `data.items[].llm_model_id` |
| `stt.provider`, `stt.model` | `stt.provider_id`, `stt.model_id` | [List STT providers](https://docs.verbex.ai/api-reference/speech-providers/list-stt-providers.md): `data[].id` and `data[].languages[].models[].id` |
| `tts.provider`, `tts.voice_name`, `tts.model_name` | `tts.provider_id`, `tts.voice_id` | [List TTS providers](https://docs.verbex.ai/api-reference/speech-providers/list-tts-providers.md): `data[].id` and `data[].languages[].voices[].voice_id` |

`llm.system_prompt`, `llm.tools`, `llm.required_dynamic_data` and `tts.voice_temperature` are unchanged.

Provider credentials are no longer sent with the agent. A custom model's endpoint and key are part of the model catalog entry.

The name fields still come back on reads. The server copies them from the catalog when it stores the agent. Treat them as read-only.

> [!TIP]
>
> To migrate stored configuration, list each catalog once and build a name-to-id table, for example `deepgram` + `nova-2-phonecall` to `provider_id` + `model_id`. Then rewrite your stored request bodies with it. The ids are stable.

## 4. Update rejects the LLM name fields

[Update AI Agent](https://docs.verbex.ai/api-reference/ai-agents-v3/update-ai-agent.md) returns `400` when the body contains `llm.model_provider`, `llm.model_name` or `llm.model_display_name`. Send `llm.llm_id` to change the model.

```json theme={null}
{
  "status": "error",
  "message": "Agent validation failed",
  "error_details": {
    "llm.model_name": "llm.model_name is not accepted on v3 update; send llm.llm_id to change the model"
  }
}
```

> [!WARNING]
>
> [Get AI Agent](https://docs.verbex.ai/api-reference/ai-agents-v3/get-ai-agent.md) returns those three fields inside `data.llm`. A client that reads an agent and sends the same `llm` object back gets this `400`. Remove the three fields first, or send only the fields you change.

## 5. Agentic agents cannot be created

`llm_type: "agentic"` returns `422`. This already applies on v2. Use `flow` for new multi-step agents. Existing agentic agents stay readable and updatable.

## 6. Flow agents carry their selections in the graph

For `llm_type: "flow"`, the root `llm` object holds only `llm_type` and `graph_data`. The root `stt` and `tts` objects are ignored. The flow-default references live in `graph_data.agentDefaults`:

```json theme={null}
"agentDefaults": {
  "stt": { "provider_id": "…", "model_id": "…" },
  "llm": { "llm_id": "…" },
  "tts": { "provider_id": "…", "voice_id": "…" }
}
```

In responses, the root `llm` of a flow agent also carries `llm_id` and the model fields, filled from the flow-default model. A flow agent has no root `stt`, `tts`, `agent_owner_id` or `pronunciations`.

On update, `graph_data` is not merged: a sent graph replaces the stored one. Send `llm_type: "flow"` with the complete graph.

## 7. Null handling differs between list and get

`GET /v3/ai-agents/{agent_id}` omits fields that have no value. List items include every field, with an explicit `null`. If you read both into the same type, make every field optional and treat a missing field and `null` the same way.

## What has not changed

* The agent's own field names and meanings, and snake\_case JSON.
* Cursor pagination with `page_size`, `page_token` and `next_page_token`.
* The list filters and tenant scoping.
* Validation errors return `422`. Out-of-range list parameters return `422`.

## Operations with no v3 equivalent yet

If your integration uses these operations, keep calling them on v2 and migrate the CRUD calls separately:

* `GET /v2/ai-agents/by-workspaces`
* `GET /v2/ai-agents/metrics/concurrent`
* The agent versions API under `/v2/ai-agents/{agent_id}/versions`


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