> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.voicebip.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.voicebip.com/_mcp/server.

# Conversations

> List, inspect, hand off, and stream messaging conversations across SMS and WhatsApp.

A **conversation** is a stateful thread of messages between one of your agents and one external number. Conversations span both SMS and WhatsApp channels — the platform threads inbound and outbound messages onto the same conversation when they share an agent + remote-number pair.

| Concept                      | What it is                                                                                                                   |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `conversation_id` (`conv_*`) | Stable ID for the thread. Returned on every `message.received` and `message.sent` event.                                     |
| `ai_mode`                    | Who is currently responding: `ai`, `human`, or `closed`.                                                                     |
| Window                       | For WhatsApp only: rolling 24-hour limit on free-form outbound. Templates bypass it. See [WhatsApp guide](/guides/whatsapp). |

This guide covers list/get, the AI ↔ human handoff via `PATCH /v1/conversations/{id}`, and live updates via SSE.

## List Conversations

```bash
curl "https://api.voicebip.com/v1/conversations?page_size=20" \
  -H "Authorization: Bearer pk_live_your_key"
```

Filter by `agent_id` or `channel` (`sms` | `whatsapp`). Paginate with `?page_token=...` returned by the previous response. See [Pagination](/reference/pagination) for the cursor format.

**Response — 200 OK**

```json
{
  "conversations": [
    {
      "conversation_id": "conv_abc123",
      "agent_id": "agt_PAEZ_njcfm2kycpjs",
      "channel": "whatsapp",
      "from_number": "+2348031234567",
      "to_number": "+2349012345678",
      "window_expires_at": "2026-05-16T14:32:01Z",
      "opt_out": false,
      "created_at": "2026-05-15T10:15:00Z",
      "updated_at": "2026-05-15T14:32:01Z",
      "last_message_at": "2026-05-15T14:32:01Z"
    }
  ],
  "next_page_token": "eyJ..."
}
```

`window_expires_at` is WhatsApp-only — `null` for SMS conversations. To inspect the current `ai_mode` for a conversation, fetch it via `GET /v1/conversations/{conversation_id}` — the list endpoint omits it to keep the listing query cheap.

## Get a Single Conversation

Returns full message history (paginated reverse-chronological) and metadata.

```bash
curl "https://api.voicebip.com/v1/conversations/conv_abc123" \
  -H "Authorization: Bearer pk_live_your_key"
```

The response includes the message thread plus the current `ai_mode`, `created_at`, and the WhatsApp window state if applicable.

## Hand Off to a Human (AI → Human)

Operator dashboards take over an active AI conversation by PATCHing the mode:

```bash
curl -X PATCH "https://api.voicebip.com/v1/conversations/conv_abc123" \
  -H "Authorization: Bearer pk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{ "ai_mode": "human" }'
```

| Transition   | Effect                                                                                                                                     |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `ai → human` | Voicebip stops dispatching new inbound messages to the agent's BYOM webhook / hosted AI. Operators reply via `POST /v1/messages` as usual. |
| `human → ai` | AI dispatcher resumes — the next inbound message is delivered to the agent.                                                                |
| `* → closed` | Terminal. The next inbound message from this remote number starts a brand-new conversation.                                                |

**Idempotency.** PATCHing the same `ai_mode` returns the current state with `changed: false` and does **not** publish a `conversation.mode_changed` event — safe to call from a click handler that may double-fire.

**Audit trail.** Every transition writes a row to `conversation_mode_history` with `actor_user_id`, `from_mode`, `to_mode`, and timestamp. Operators can see who handed off when.

## Live Updates via SSE

Subscribe to message and mode-change events for a single conversation:

```javascript
const es = new EventSource(
  `https://api.voicebip.com/v1/conversations/${conversationId}/stream?token=${apiKey}`
);

es.onmessage = (frame) => {
  const event = JSON.parse(frame.data);
  // event.event_type: message.received | message.delivered | message.read | conversation.mode_changed
  // event.conversation_id
  // event.timestamp (ISO 8601)
  // event.payload — event-specific fields (message_id, direction, text, from_mode, to_mode, etc.)
};
```

Browsers can't set custom headers on `EventSource`, so authenticate via `?token=` (server only accepts the query param on this specific path). For server-side consumers use `Authorization: Bearer`. Full stream reference: [Live Streaming](/reference/streaming).

## Export an Agent's Call Transcripts (CSV / JSON)

For offline analysis or backup. Note this endpoint exports the agent's **voice
call** transcripts (call metadata + transcript text), not SMS/WhatsApp message
threads:

```bash
curl "https://api.voicebip.com/v1/agents/agt_PAEZ_njcfm2kycpjs/conversations/export?format=csv" \
  -H "Authorization: Bearer pk_live_your_key" \
  -o transcripts.csv
```

`format` accepts `csv` or `json` (defaults to `json`). The CSV columns are
`call_id, from_number, to_number, direction, status, duration_seconds, created_at, transcript_text`.
The endpoint returns up to the 1,000 most-recent calls for the agent. For larger exports use [the deliveries replay API](/guides/webhooks#webhook-delivery-log) or contact support.

## Closing a Conversation

```bash
curl -X PATCH "https://api.voicebip.com/v1/conversations/conv_abc123" \
  -H "Authorization: Bearer pk_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{ "ai_mode": "closed" }'
```

`closed` is terminal. The next inbound message from the same remote number to the same agent will create a fresh `conv_*` ID.

## Webhook Events for Conversations

Conversation activity surfaces through the normal messaging webhook events: `message.received`, `message.delivered`, `message.read` (WhatsApp), `message.opt_out`, and `window.closing`. See [Webhooks](/guides/webhooks) for the full event reference.

The `conversation.mode_changed` event is an **internal** subject today — it fires on `PATCH /v1/conversations/{id}` but is not delivered to your webhook URL. The SSE stream above is the recommended way to subscribe to mode changes.