---
title: "Agents and Chat"
description: "List your agents and chat with them through the Chatvox API, including continuing conversations and linking them to your own users."
---

## The agent object

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | The agent's ID. |
| `name` | string | The agent's name. |
| `status` | string | `active` or `inactive`. An agent becomes inactive when your team has more agents than your plan allows, and can't answer messages until you upgrade or remove another agent. |
| `created_at` | string | When the agent was created. |

Only agents that have finished setup are returned. A key with **Selected agents** access only sees those agents.

## List agents

```http
GET /api/v1/agents
```

```bash
curl https://my.chatvox.co/api/v1/agents \
  -H "Authorization: Bearer cvx_your_key"
```

Returns a [paginated list](/docs/developers#lists-and-pagination) of agents, newest first.

## Get an agent

```http
GET /api/v1/agents/{agent}
```

```json
{
  "id": "019f80ad-e549-712d-bc26-3ab29ae2228c",
  "name": "Support agent",
  "status": "active",
  "created_at": "2026-07-20T17:58:27+00:00"
}
```

## Send a message

```http
POST /api/v1/agents/{agent}/messages
```

Sends a message to the agent and returns its reply. The agent uses its instructions, knowledge, and actions just like on your website.

| Field | Required | Description |
| --- | --- | --- |
| `message` | Yes | The user's message, up to 4,000 characters. |
| `conversation_id` | No | Continue an existing conversation. Leave it out to start a new one. |
| `external_user_id` | No | Your own ID for the user, up to 255 characters. Saved when the conversation starts so you can [find their conversations](/docs/developers/conversations#filter-by-your-user-id) later. |

:::note[Actions in API conversations]
The agent answers from its instructions and knowledge, and can use [Custom API](/docs/actions/custom-api) and [MCP Server](/docs/actions/mcp-server) actions that have their **API** channel switched on (on by default for Custom API, off for MCP Server). **Collect Leads, Escalate to Human, Web Search, and Slack notifications don't run** in API conversations, because those actions are turned on per channel and API conversations don't belong to one. Your app is in charge of collecting details and routing customers to your team. See [Where actions run](/docs/actions#where-actions-run).
:::

**curl**

```bash
curl -X POST https://my.chatvox.co/api/v1/agents/AGENT_ID/messages \
  -H "Authorization: Bearer cvx_your_key" \
  -H "Content-Type: application/json" \
  -d '{"message": "What are your opening hours?", "external_user_id": "user-42"}'
```

**JavaScript**

```js
const response = await fetch(
  `https://my.chatvox.co/api/v1/agents/${agentId}/messages`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.CHATVOX_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      message: "What are your opening hours?",
      conversation_id: conversationId ?? undefined,
    }),
  },
);

const { conversation, reply } = await response.json();
// Save conversation.id and send it with the next message.
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$response = Http::withToken(env('CHATVOX_API_KEY'))
    ->post("https://my.chatvox.co/api/v1/agents/{$agentId}/messages", [
        'message' => 'What are your opening hours?',
        'conversation_id' => $conversationId, // null starts a new conversation
    ])
    ->throw()
    ->json();

$reply = $response['reply']['content'] ?? null;
$conversationId = $response['conversation']['id'];
```

**Python**

```python
import os
import requests

response = requests.post(
    f"https://my.chatvox.co/api/v1/agents/{agent_id}/messages",
    headers={"Authorization": f"Bearer {os.environ['CHATVOX_API_KEY']}"},
    json={"message": "What are your opening hours?", "conversation_id": conversation_id},
    timeout=60,
)
response.raise_for_status()
data = response.json()
conversation_id = data["conversation"]["id"]
```

The response is `201 Created`:

```json
{
  "conversation": {
    "id": "01a121f0-6be6-700e-bda0-9860a935556f",
    "agent_id": "019f80ad-e549-712d-bc26-3ab29ae2228c",
    "contact_id": null,
    "title": "New Chat",
    "state": "ai_active",
    "source": "api",
    "external_user_id": "user-42",
    "last_message_preview": "We're open Monday to Friday, 9am to 5pm.",
    "last_active_at": "2026-10-09T18:32:52+00:00",
    "created_at": "2026-10-09T18:32:46+00:00"
  },
  "reply": {
    "id": 726,
    "role": "assistant",
    "origin": "ai",
    "content": "We're open Monday to Friday, 9am to 5pm.",
    "created_at": "2026-10-09T18:32:52+00:00"
  },
  "usage": { "credits": 1 }
}
```

- `reply.content` is the agent's answer, in Markdown.
- `usage.credits` is the number of credits the reply used.

### Continue a conversation

Every message without a `conversation_id` starts a **new** conversation, and the agent won't remember earlier messages. To keep the context, store the `conversation.id` from the first response, for example per user in your database, and send it with every following message:

```json
{ "message": "And on weekends?", "conversation_id": "01a121f0-6be6-700e-bda0-9860a935556f" }
```

You can only continue conversations that were started through the API. Conversations from your website, Public Chat, or WhatsApp return `404`.

### Retry safely with an idempotency key

If a request times out, you can't tell whether the agent already answered. Send an `Idempotency-Key` header with a unique value for each message, such as a UUID or your own message ID, and reuse it when you retry:

```bash
curl -X POST https://my.chatvox.co/api/v1/agents/AGENT_ID/messages \
  -H "Authorization: Bearer cvx_your_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 5f0c8a1e-2b7d-4e9a-9c41-7d2e6b3a8f10" \
  -d '{"message": "What are your opening hours?"}'
```

- If the first request finished, a retry with the same key returns the **same response** with the header `Idempotent-Replayed: true`. The message isn't sent again and no extra credits are used.
- If the first request is still running, the retry returns `409 idempotency_in_progress`. Wait a few seconds and retry again.
- If the first request failed, the key isn't kept, so the retry sends the message normally.
- Using the same key for a different message returns `422 idempotency_key_reused`. Use a new key for every new message.
- Keys can be up to 255 characters, belong to the API key that sent them, and are kept for 24 hours.

### Good to know

- Replies usually take a few seconds. Use a request timeout of at least 60 seconds.
- Send one message at a time per conversation. A second message while the agent is still answering returns `409 conversation_busy` and isn't saved, so you can send it again once the reply arrives.
- API conversations appear in your agent's **Logs** with the source **API**.
