# Escalate a conversation to an agent

In this setup we expect you to have your own widget for the end user to start the conversation. You will use the import conversation endpoint to escalate to an agent, or to add a closed conversation to Dixa for analytics and auto QA.

## Prerequisites

Before you start, you need the following:

**Bot user (agentId + API token)**
A bot user represents your third-party AI agent inside Dixa. It is created by a Dixa administrator in **Settings > AI agents > Third-party AI agents**. Upon creation, Dixa issues a JWT — this JWT is the API token used to authenticate all API calls in this guide, and the `agentId` UUID is embedded in it. The `agentId` is also the author of messages sent by the chatbot before the handoff.

**Queue ID (queueId)**
The queue to route escalated conversations to. Find your queue IDs in **Settings > Queues**, or retrieve them via the [List queues](https://docs.dixa.io/openapi/dixa-api/v1/queues/getqueues) API. Make sure to use a queue staffed by human agents.

**Webhook endpoint**
A URL in your system that can receive `POST` requests from Dixa. See [Step 4](#step-4--listen-for-webhook-events) for event configuration.

## Happy path flow


```mermaid
sequenceDiagram
    actor User as End User
    participant Bot as Chatbot
    participant API as Dixa API
    participant Agent as Agent (Dixa)
    participant WH as Webhook listener

    User->>Bot: Requests human agent
    Bot->>API: Import conversation<br/>(POST /conversations/import)
    API-->>Bot: conversationId
    Bot->>API: Transfer to queue<br/>(PUT /conversations/{id}/transfer/queue)
    API->>Agent: Conversation assigned from queue
    Agent->>API: Sends reply message
    API->>WH: Webhook: message_added event
    WH->>Bot: Forward message
    Bot->>User: Display agent reply
    Agent->>API: Closes conversation
    API->>WH: Webhook: conversation_closed event
    WH->>Bot: Notify closure
    Bot->>User: Show conversation ended
```

## Step-by-step walkthrough

### Step 1 — Look up the end-user ID

[API reference: List end-users](https://docs.dixa.io/openapi/dixa-api/v1/end-users/getendusers)

Search by the end-user's email address (or phone / external ID) to retrieve their Dixa UUID. Use the `id` from the response as `requesterId` in the next step.

If no match is found, create the end-user first using the [Create end-user](https://docs.dixa.io/openapi/dixa-api/v1/end-users/postendusers) API, then use the returned `id`.

### Step 2 — Import the conversation

[API reference: Import conversation](https://docs.dixa.io/openapi/dixa-api/v1/conversations/postconversationsimport)

Pass the chat history so the agent has context before they take over. Set `genericChannelName` to the fixed value `"genericapimessaging"` (this is always the literal string for this channel), `requesterId` to the end-user ID from Step 1, and `agentId` to your bot user's ID. Save the numeric `id` from the response — this is the `conversationId` used in the next steps.

### Step 3 — Transfer the conversation to an agent queue

[API reference: Transfer conversation to queue](https://docs.dixa.io/openapi/dixa-api/v1/conversations/putconversationsconversationidtransferqueue)

This moves the conversation out of bot ownership and into the human-staffed queue, making it visible to agents. A `204 No Content` response means the transfer succeeded.

### Step 4 — Listen for webhook events

Webhooks are configured in the Dixa UI. Go to **Settings > Integrations > Webhooks**, click **Add outbound webhook**, and fill in your endpoint URL. See the [webhook setup guide](/docs/webhooks/webhooks-setup) for the full walkthrough.

Webhooks can also be created programmatically via the [Create webhook](/openapi/dixa-api/v1/webhooks/postwebhooks) API. This is the recommended approach if you are productising the integration — every new customer gets the same webhook events and filters without manual setup.

Under **Filters**, set the channel filter to **Generic API Messaging** so you only receive events from this channel. Then subscribe to the following events:

| Event | When it fires | What your chatbot should do |
|  --- | --- | --- |
| **Message added** | Agent sends a reply | Extract `data.text` and forward it to the end user |
| **Conversation closed** | Agent closes the conversation | Notify the end user; optionally restart the bot flow |
| **Typing started** *(optional)* | Agent is typing | Show a typing indicator on your widget |


See the [Webhook payloads reference](/docs/webhooks/webhooks-payloads) for the payload structure of each event.

## Error handling

### End-user not found

`GET /v1/endusers` returns an empty `data` array (not a `404`) when no match is found. Follow the guidance in [Step 1](#step-1--look-up-the-end-user-id) to create the end-user before importing the conversation.

### Import conversation fails

A `400` response from `POST /v1/conversations/import` will include a message indicating the cause:

| Message | Cause | Fix |
|  --- | --- | --- |
| `Requester does not exist` | `requesterId` is not a valid end-user UUID | Re-run Step 1 to get the correct ID |
| `Agent does not exist` | `agentId` is not a valid bot user UUID | Verify the UUID from the JWT token issued at bot user creation |
| `Unsupported ConversationChannel` | `genericChannelName` is not `"genericapimessaging"` | Use the exact literal string `"genericapimessaging"` |


A `201` response does not guarantee full success — always check `data.partialErrors` in the response body. Partial errors are non-fatal and indicate that specific messages or the assignment could not be processed.

### Transfer to queue fails

A `400` response from `PUT /v1/conversations/{conversationId}/transfer/queue` will include a message indicating the cause:

| Message | Cause | Fix |
|  --- | --- | --- |
| `Queue does not exist` | `queueId` is not a valid queue UUID | Verify the queue ID via [List queues](https://docs.dixa.io/openapi/dixa-api/v1/queues/getqueues) |
| `Invalid value for: path parameter conversationId` | `conversationId` is not a valid integer | Use the numeric `id` returned by Step 2, not a UUID |


A `404` response means the conversation was not found — check that the `conversationId` from Step 2 was saved correctly.

## Resuming the bot after the agent closes the conversation

When a `CONVERSATION_CLOSED` webhook fires, the `data.conversation.id` in the payload identifies the conversation that was closed. See the [Webhook payloads reference](/docs/webhooks/webhooks-payloads) for the full payload structure.

From there, your chatbot has two options:

**Option A — Reopen the same conversation**

[API reference: Reopen conversation](https://docs.dixa.io/openapi/dixa-api/v1/conversations/putconversationsconversationidreopen)

Call `PUT /v1/conversations/{conversationId}/reopen` to put the conversation back into the same queue it came from. Use this when the end user has a follow-up on the same topic.

**Option B — Start a new conversation**

Call `POST /v1/conversations/import` again (Step 2) with a fresh message history. Use this when the end user starts a new, unrelated topic.

## Add custom attributes to the conversation (optional)

[API reference: Patch custom attributes](https://docs.dixa.io/openapi/dixa-api/v1/custom-attributes/patchconversationsconversationidcustom-attributes)

Attach metadata such as order numbers or customer tier to the conversation. Keys are custom attribute UUIDs — retrieve yours with the [List custom attributes](https://docs.dixa.io/openapi/dixa-api/v1/custom-attributes/getcustom-attributes) endpoint.