> For the complete documentation index, see [llms.txt](https://docs.maiagent.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.maiagent.ai/api/en/api-reference/dui-hua-he-xun-xi.md).

# Conversations and Messages

### Send Message (Streaming) <a href="#chatbots-completions-create" id="chatbots-completions-create"></a>

POST `/api/v1/chatbots/{chatbotId}/completions/`

#### Parameters

| Parameter Name | Required | Type   | Description |
| -------------- | -------- | ------ | ----------- |
| `chatbotId`    | ✅        | string |             |

#### Request Body

**Request Parameters**

| Field                         | Type                                                                  | Required | Description                                                                                                                                                                                                                                                                                                         |
| ----------------------------- | --------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| conversation                  | string (uuid)                                                         | No       | Unique conversation identifier; a new conversation is created if empty (optional)                                                                                                                                                                                                                                   |
| message                       | object (with 9 properties: content, contentPayload, queryMetadata...) | Yes      | Message to send                                                                                                                                                                                                                                                                                                     |
| message.content               | string                                                                | Yes      | Message text; can be an empty string if attachments are provided                                                                                                                                                                                                                                                    |
| message.contentPayload        | object                                                                | No       | Additional message payload in JSON format (optional)                                                                                                                                                                                                                                                                |
| message.queryMetadata         | object                                                                | No       | Query metadata in JSON format (optional)                                                                                                                                                                                                                                                                            |
| message.metadata              | object                                                                | No       | Message environment metadata, such as timezone information, in JSON format (optional)                                                                                                                                                                                                                               |
| message.attachments           | array\[AttachmentInput]                                               | No       | List of message attachments (optional)                                                                                                                                                                                                                                                                              |
| message.toolIds               | array\[string]                                                        | No       | List of tool IDs to enable for this message (optional)                                                                                                                                                                                                                                                              |
| message.clientTools           | array\[any]                                                           | No       | List of tool definitions provided by the client (such as a Chrome extension) for this message (optional). Each entry must include name(str), description(str), and input\_schema(dict). When the LLM calls a tool, the backend returns the clientToolCall content and ends the turn without executing the tool.     |
| message.sender                | string (uuid)                                                         | No       | Sender's Contact ID (optional); mutually exclusive with externalUser                                                                                                                                                                                                                                                |
| message.externalUser          | object (with 4 properties: id, name, email...)                        | No       | Ask a question as a user of the calling system (optional): MaiAgent finds or creates a contact by external user ID and associates the conversation with that contact. Mutually exclusive with sender; providing both returns 400                                                                                    |
| message.externalUser.id       | string                                                                | Yes      | User ID in the calling system (required; exact, case-sensitive string matching; empty or whitespace-only strings return 400). MaiAgent finds or creates a contact by (organization, external user ID). If an organization uses multiple external systems, a system prefix is recommended, such as projects:usr\_123 |
| message.externalUser.name     | string                                                                | No       | Contact name (optional); updated when provided, unchanged when omitted, null, or an empty string. For a new contact, defaults to the external user ID if omitted                                                                                                                                                    |
| message.externalUser.email    | object                                                                | No       | Contact email (optional); updated when provided, unchanged when omitted, null, or an empty string. A nonempty value with an invalid format returns 400                                                                                                                                                              |
| message.externalUser.metadata | object                                                                | No       | Custom contact information (optional), using the same format as metadata in the Contact API; fully replaced when provided, unchanged when omitted or null                                                                                                                                                           |
| isStreaming                   | boolean                                                               | No       | Whether to stream the response; defaults to false (optional)                                                                                                                                                                                                                                                        |
| waitForAttachments            | boolean                                                               | No       | Whether to wait for attachment processing to finish before entering the agent workflow; defaults to true (optional)                                                                                                                                                                                                 |

**Request Schema Example**

```typescript
{
  "conversation"?: string (uuid) // Unique conversation identifier; a new conversation is created if empty (optional) (Optional)
  "message":  // Message to send
  {
    "content": string // Message text; can be an empty string if attachments are provided
    "contentPayload"?: object // Additional message payload in JSON format (optional) (Optional)
    "queryMetadata"?: object // Query metadata in JSON format (optional) (Optional)
    "metadata"?: object // Message environment metadata, such as timezone information, in JSON format (optional) (Optional)
    "attachments"?: [ // List of message attachments (optional) (Optional)
      {
        "id": string (uuid) // Unique attachment identifier
        "type":  // Attachment type; available values: image (image), video (video, in development, not yet supported), audio (audio), sticker (sticker, in development, not yet supported), other (other)

* `image` - Image
* `video` - Video
* `audio` - Audio
* `sticker` - Sticker
* `other` - Other
        {
        }
        "filename": string // Attachment filename
        "file": string (uri) // Attachment file URL
      }
    ]
    "toolIds"?: [ // List of tool IDs to enable for this message (optional) (Optional)
      string (uuid)
    ]
    "clientTools"?: [ // List of tool definitions provided by the client (such as a Chrome extension) for this message (optional). Each entry must include name(str), description(str), and input_schema(dict). When the LLM calls a tool, the backend returns the clientToolCall content and ends the turn without executing the tool. (Optional)
      object
    ]
    "sender"?: string (uuid) // Sender's Contact ID (optional); mutually exclusive with externalUser (Optional)
    "externalUser"?:  // Ask a question as a user of the calling system (optional): MaiAgent finds or creates a contact by external user ID and associates the conversation with that contact. Mutually exclusive with sender; providing both returns 400 (Optional)
    {
      "id": string // User ID in the calling system (required; exact, case-sensitive string matching; empty or whitespace-only strings return 400). MaiAgent finds or creates a contact by (organization, external user ID). If an organization uses multiple external systems, a system prefix is recommended, such as projects:usr_123
      "name"?: string // Contact name (optional); updated when provided, unchanged when omitted, null, or an empty string. For a new contact, defaults to the external user ID if omitted (Optional)
      "email"?: // May have different types (Optional)
      string (email) // Contact email (optional); updated when provided, unchanged when omitted, null, or an empty string. A nonempty value with an invalid format returns 400 (Optional)
      "metadata"?: object // Custom contact information (optional), using the same format as metadata in the Contact API; fully replaced when provided, unchanged when omitted or null (Optional)
    }
  }
  "isStreaming"?: boolean // Whether to stream the response; defaults to false (optional) (Optional)
  "waitForAttachments"?: boolean // Whether to wait for attachment processing to finish before entering the agent workflow; defaults to true (optional) (Optional)
}
```

**Example Request Values**

```json
{
  "message": {
    "content": "Hello, please introduce yourself"
  },
  "is_streaming": true
}
```

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# Example API Call (Shell)
curl -X POST "https://api.maiagent.ai/api/v1/chatbots/550e8400-e29b-41d4-a716-446655440000/completions/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message": {
      "content": "Hello, please introduce yourself"
    },
    "is_streaming": true
  }'

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
};

// Request Body (payload)
const data = {
    "message": {
      "content": "Hello, please introduce yourself"
    },
    "is_streaming": true
  };

axios.post("https://api.maiagent.ai/api/v1/chatbots/550e8400-e29b-41d4-a716-446655440000/completions/", data, config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/chatbots/550e8400-e29b-41d4-a716-446655440000/completions/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request Body (payload)
data = {
    "message": {
        "content": "Hello, please introduce yourself"
    },
    "is_streaming": True
}

response = requests.post(url, json=data, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->post("https://api.maiagent.ai/api/v1/chatbots/550e8400-e29b-41d4-a716-446655440000/completions/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'message' => [
                'content' => 'Hello, please introduce yourself'
            ],
            'is_streaming' => true
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

| Status Code | Description                                                                                                                                                                                                                                                                                               |
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 200         | Conversation response content; an event stream when streaming is enabled                                                                                                                                                                                                                                  |
| 202         | Attachments are still being indexed: the body \`status\` is \`pending\_attachments\`; retry later.                                                                                                                                                                                                        |
| 400         | Invalid request parameters; possible causes include: the Contact ID does not belong to the organization Error codes (X-Error-Code): \`contact\_not\_found\`, \`contact\_organization\_mismatch\`, \`contact\_inbox\_not\_linked\`, \`contact\_permission\_denied\`, \`message\_content\_required\`, \`... |
| 401         | Error codes (X-Error-Code): \`not\_authenticated\`, \`authentication\_failed\`.                                                                                                                                                                                                                           |
| 402         | Error codes (X-Error-Code): \`insufficient\_credits\`, \`insufficient\_quota\`, \`no\_active\_plan\`.                                                                                                                                                                                                     |
| 403         | Error codes (X-Error-Code): \`permission\_denied\`.                                                                                                                                                                                                                                                       |
| 404         | Error codes (X-Error-Code): \`conversation\_not\_found\`, \`not\_found\`.                                                                                                                                                                                                                                 |
| 429         | Error codes (X-Error-Code): \`throttled\`.                                                                                                                                                                                                                                                                |

***

### Send Message (Create) <a href="#messages-create" id="messages-create"></a>

POST `/api/v1/messages/`

#### Request Body

**Request Parameters**

| Field                                   | Type                                                         | Required | Description                                                                                                                                                                                                                                                |
| --------------------------------------- | ------------------------------------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| conversation                            | string (uuid)                                                | Yes      |                                                                                                                                                                                                                                                            |
| type                                    | string                                                       | No       |                                                                                                                                                                                                                                                            |
| content                                 | string                                                       | No       |                                                                                                                                                                                                                                                            |
| contentPayload                          | object                                                       | No       |                                                                                                                                                                                                                                                            |
| attachments                             | array\[MessageAttachmentInput]                               | No       |                                                                                                                                                                                                                                                            |
| canvas                                  | object                                                       | No       |                                                                                                                                                                                                                                                            |
| canvas.name                             | string                                                       | Yes      |                                                                                                                                                                                                                                                            |
| canvas.canvasType                       | object                                                       | Yes      |                                                                                                                                                                                                                                                            |
| canvas.title                            | string                                                       | Yes      |                                                                                                                                                                                                                                                            |
| canvas.content                          | string                                                       | Yes      |                                                                                                                                                                                                                                                            |
| slideTemplate                           | object (with 3 properties: slug, displayName, thumbnailUrls) | No       |                                                                                                                                                                                                                                                            |
| slideTemplate.slug                      | string                                                       | Yes      |                                                                                                                                                                                                                                                            |
| slideTemplate.displayName               | string                                                       | Yes      |                                                                                                                                                                                                                                                            |
| slideTemplate.thumbnailUrls             | array\[string]                                               | No       |                                                                                                                                                                                                                                                            |
| queryMetadata                           | object                                                       | No       | Query metadata controlling the scope of searchable knowledge; see each endpoint's documentation for details                                                                                                                                                |
| queryMetadata.knowledgeBases            | array\[object]                                               | No       | List of searchable knowledge bases; an empty array or null means none are accessible (no permission); omitted means unrestricted                                                                                                                           |
| queryMetadata.labelRelations            | object                                                       | No       | Label filters; an empty conditions array means no label filtering (not a lack of permission). Must be provided together with knowledgeBases. If provided alone, label filtering is not applied to knowledge base retrieval (equivalent to no restrictions) |
| queryMetadata.labelRelations.operator   | string (enum: AND, OR)                                       | Yes      | How to combine label conditions                                                                                                                                                                                                                            |
| queryMetadata.labelRelations.conditions | array\[any]                                                  | No       | List of label conditions; supports nested operator/conditions combinations                                                                                                                                                                                 |
| metadata                                | object                                                       | No       | Message metadata, such as timezone and other environment information                                                                                                                                                                                       |
| broadcast                               | boolean                                                      | No       |                                                                                                                                                                                                                                                            |
| skipCopilotTrigger                      | boolean                                                      | No       |                                                                                                                                                                                                                                                            |
| suggestedForId                          | string (uuid)                                                | No       |                                                                                                                                                                                                                                                            |
| skillIds                                | array\[string]                                               | No       | Per-message selected skill IDs from the WebChat client (camelCase: skillIds).                                                                                                                                                                              |
| toolIds                                 | array\[string]                                               | No       | Per-message selected tool IDs from the WebChat client (camelCase: toolIds).                                                                                                                                                                                |
| templateId                              | string (uuid)                                                | No       | Document/sheet template ID for file-level compilation (camelCase: templateId).                                                                                                                                                                             |

**Request Schema Example**

```typescript
{
  "conversation": string (uuid)
  "type"?: string // Optional
  "content"?: string // Optional
  "contentPayload"?: object // Optional
  "attachments"?: [ // Optional
    {
      "id": string (uuid) // ID of an already-uploaded attachment, as returned by the attachment upload endpoint. Required: a missing, null or non-UUID id is rejected with HTTP 400 and the message is not created.
      "type"?:  // Optional
      {
      }
      "filename": string
      "file": string (uri)
      "conversation"?: string (uuid) // Optional
    }
  ]
  "canvas"?: { // Optional
  {
    "name": string
    "canvasType": 
    {
    }
    "title": string
    "content": string
  }
  }
  "slideTemplate"?:  // Optional
  {
    "slug": string
    "displayName": string
    "thumbnailUrls"?: [ // Optional
      string
    ]
  }
  "queryMetadata"?: { // Query metadata controlling the scope of searchable knowledge; see each endpoint's documentation for details (Optional)
  {
    "knowledgeBases"?: [ // List of searchable knowledge bases; an empty array or null means none are accessible (no permission); omitted means unrestricted (Optional)
      {
        "knowledgeBaseId": string (uuid) // Knowledge base ID
        "chatbotFileIds"?: [ // List of file IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "knowledgeBaseFileIds"?: [ // List of file IDs (the new name for chatbotFileIds; this field takes precedence when both are provided) (Optional)
          string (uuid)
        ]
        "faqIds"?: [ // List of FAQ IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "hasUserSelectedAll"?: boolean // true selects all knowledge base content except the listed items; false selects only the listed items (Optional)
      }
    ]
    "labelRelations"?: { // Label filters; an empty conditions array means no label filtering (not a lack of permission). Must be provided together with knowledgeBases. If provided alone, label filtering is not applied to knowledge base retrieval (equivalent to no restrictions) (Optional)
    {
      "operator": string (enum: AND, OR) // How to combine label conditions
      "conditions"?: [ // List of label conditions; supports nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "metadata"?: object // Message metadata, such as timezone and other environment information (Optional)
  "broadcast"?: boolean // Optional
  "skipCopilotTrigger"?: boolean // Optional
  "suggestedForId"?: string (uuid) // Optional
  "skillIds"?: [ // Per-message selected skill IDs from the WebChat client (camelCase: skillIds). (Optional)
    string (uuid)
  ]
  "toolIds"?: [ // Per-message selected tool IDs from the WebChat client (camelCase: toolIds). (Optional)
    string (uuid)
  ]
  "templateId"?: string (uuid) // Document/sheet template ID for file-level compilation (camelCase: templateId). (Optional)
}
```

**Example Request Values**

```json
{
  "conversation": "c96a0fb9-d106-4b16-8706-3906533bafa2",
  "sender": {
    "id": 2.992461161002561e+38,
    "name": "jessiekuo",
    "avatar": "https://media-dev.maiagent.ai/static/images/default-user-avatar.png"
  },
  "type": "incoming",
  "content": "This is a test message",
  "contentPayload": {
    "content": "This is a test message",
    "items": [
      {
        "type": "text",
        "text": "This is a test message",
        "startTimestamp": null,
        "citations": []
      }
    ],
    "metadata": null
  },
  "feedback": null,
  "createdAt": "1748314926000",
  "attachments": [],
  "citations": [],
  "citationNodes": [],
  "canvas": {
    "id": "034eecd7-e0f4-46e9-88cb-4fefdb5b7ddd",
    "name": "system-architecture-diagram",
    "canvasType": "markdown",
    "title": "System Architecture Diagram",
    "content": "~~~mermaid\ngraph TD\n    A[User] --> B[Frontend Interface]\n    B --> C[API Layer]\n    C --> D[Database]\n~~~",
    "createdAt": "1748314926000"
  }
}
```

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# Example API Call (Shell)
curl -X POST "https://api.maiagent.ai/api/v1/messages/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "conversation": "c96a0fb9-d106-4b16-8706-3906533bafa2",
    "sender": {
      "id": 2.992461161002561e+38,
      "name": "jessiekuo",
      "avatar": "https://media-dev.maiagent.ai/static/images/default-user-avatar.png"
    },
    "type": "incoming",
    "content": "This is a test message",
    "contentPayload": {
      "content": "This is a test message",
      "items": [
        {
          "type": "text",
          "text": "This is a test message",
          "startTimestamp": null,
          "citations": []
        }
      ],
      "metadata": null
    },
    "feedback": null,
    "createdAt": "1748314926000",
    "attachments": [],
    "citations": [],
    "citationNodes": [],
    "canvas": {
      "id": "034eecd7-e0f4-46e9-88cb-4fefdb5b7ddd",
      "name": "system-architecture-diagram",
      "canvasType": "markdown",
      "title": "System Architecture Diagram",
      "content": "~~~mermaid\ngraph TD\n    A[User] --> B[Frontend Interface]\n    B --> C[API Layer]\n    C --> D[Database]\n~~~",
      "createdAt": "1748314926000"
    }
  }'

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
};

// Request Body (payload)
const data = {
    "conversation": "c96a0fb9-d106-4b16-8706-3906533bafa2",
    "sender": {
      "id": 2.992461161002561e+38,
      "name": "jessiekuo",
      "avatar": "https://media-dev.maiagent.ai/static/images/default-user-avatar.png"
    },
    "type": "incoming",
    "content": "This is a test message",
    "contentPayload": {
      "content": "This is a test message",
      "items": [
        {
          "type": "text",
          "text": "This is a test message",
          "startTimestamp": null,
          "citations": []
        }
      ],
      "metadata": null
    },
    "feedback": null,
    "createdAt": "1748314926000",
    "attachments": [],
    "citations": [],
    "citationNodes": [],
    "canvas": {
      "id": "034eecd7-e0f4-46e9-88cb-4fefdb5b7ddd",
      "name": "system-architecture-diagram",
      "canvasType": "markdown",
      "title": "System Architecture Diagram",
      "content": "~~~mermaid\ngraph TD\n    A[User] --> B[Frontend Interface]\n    B --> C[API Layer]\n    C --> D[Database]\n~~~",
      "createdAt": "1748314926000"
    }
  };

axios.post("https://api.maiagent.ai/api/v1/messages/", data, config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/messages/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request Body (payload)
data = {
    "conversation": "c96a0fb9-d106-4b16-8706-3906533bafa2",
    "sender": {
        "id": 2.992461161002561e+38,
        "name": "jessiekuo",
        "avatar": "https://media-dev.maiagent.ai/static/images/default-user-avatar.png"
    },
    "type": "incoming",
    "content": "This is a test message",
    "contentPayload": {
        "content": "This is a test message",
        "items": [
            {
                "type": "text",
                "text": "This is a test message",
                "startTimestamp": None,
                "citations": []
            }
        ],
        "metadata": None
    },
    "feedback": None,
    "createdAt": "1748314926000",
    "attachments": [],
    "citations": [],
    "citationNodes": [],
    "canvas": {
        "id": "034eecd7-e0f4-46e9-88cb-4fefdb5b7ddd",
        "name": "system-architecture-diagram",
        "canvasType": "markdown",
        "title": "System Architecture Diagram",
        "content": "~~~mermaid\ngraph TD\n    A[User] --> B[Frontend Interface]\n    B --> C[API Layer]\n    C --> D[Database]\n~~~",
        "createdAt": "1748314926000"
    }
}

response = requests.post(url, json=data, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->post("https://api.maiagent.ai/api/v1/messages/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'conversation' => 'c96a0fb9-d106-4b16-8706-3906533bafa2',
            'sender' => [
                'id' => 2.992461161002561e+38,
                'name' => 'jessiekuo',
                'avatar' => 'https://media-dev.maiagent.ai/static/images/default-user-avatar.png'
            ],
            'type' => 'incoming',
            'content' => 'This is a test message',
            'contentPayload' => [
                'content' => 'This is a test message',
                'items' => [
                    [
                        'type' => 'text',
                        'text' => 'This is a test message',
                        'startTimestamp' => null,
                        'citations' => []
                    ]
                ],
                'metadata' => null
            ],
            'feedback' => null,
            'createdAt' => '1748314926000',
            'attachments' => [],
            'citations' => [],
            'citationNodes' => [],
            'canvas' => [
                'id' => '034eecd7-e0f4-46e9-88cb-4fefdb5b7ddd',
                'name' => 'system-architecture-diagram',
                'canvasType' => 'markdown',
                'title' => 'System Architecture Diagram',
                'content' => '~~~mermaid
graph TD
    A[User] --> B[Frontend Interface]
    B --> C[API Layer]
    C --> D[Database]
~~~',
                'createdAt' => '1748314926000'
            ]
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 201**

**Response Schema Example**

```typescript
{
  "id": string (uuid)
  "conversation": string (uuid)
  "sender": 
  {
    "id": string (uuid)
    "name": string
    "avatar": string
    "email"?: string (email) // Optional
    "phoneNumber"?: string // Optional
  }
  "type"?: string // Optional
  "content"?: string // Optional
  "contentPayload"?: object // Optional
  "feedback": 
  {
    "id": string (uuid)
    "type": 
    {
    }
    "suggestion"?: string // Optional
    "updatedAt": string (timestamp)
  }
  "createdAt": string (timestamp)
  "attachments"?: [ // Optional
    {
      "id": string (uuid) // ID of an already-uploaded attachment, as returned by the attachment upload endpoint. Required: a missing, null or non-UUID id is rejected with HTTP 400 and the message is not created.
      "type"?:  // Optional
      {
      }
      "filename": string
      "file": string (uri)
      "expiresAt": string // ISO datetime when this attachment will be auto-deleted.

For conversation-bound attachments: conversation.last_message_created_at + retention_days.
For attachments without a conversation: created_at + retention_days.
Returns None when cleanup is disabled (effective_from not set).
      "conversation"?: string (uuid) // Optional
    }
  ]
  "citations": [
    {
      "id": string (uuid)
      "filename": string // File name
      "file": string (uri) // File to upload
      "fileType": string
      "knowledgeBase"?:  // Optional
      {
        "id": string (uuid)
        "name": string
      }
      "size": integer // File size in bytes
      "status": 
      {
      }
      "parser": 
      {
        "id": string (uuid)
        "name": string
        "provider": 
        {
        }
        "isTimestampSttProvider": boolean
      }
      "labels"?: [ // Optional
        {
          "id": string (uuid)
          "name": string
        }
      ]
      "rawUserDefineMetadata"?: object // Optional
      "speakerLabels": [
        {
          "id": string (uuid)
          "originalLabel": string // Original speaker label from diarization (e.g., SPEAKER_00)
          "customName"?: string // User-defined speaker name (e.g., John) (Optional)
          "displayName": string
        }
      ]
      "vectorStorageSize": integer // Size of vectors for this file in Elasticsearch (bytes)
      "chunksCount": integer // Number of chunks/nodes generated from this file
      "waitingTime": number (double)
      "processingTime": number (double)
      "processingTimeDetails": object
      "ocrStatus": string // OCR result for scanned pages: done, skipped, partial, off; null before parsing or for older files
      "ocrSkippedReason": string // Reason OCR was not performed on scanned pages: no_azure_settings, azure_unavailable, insufficient_credits, transient_error, time_budget, disabled; null when completed
      "previewUrl": string // For presentations (pptx/ppt), returns the URL of a previewable PDF derivative (displayed with the frontend PDF viewer); None for other files.

Generate URLs with the same CustomizedFileFieldSerializer as the `file` field to ensure consistent formatting (including each
storage provider's presign/host rules). Do not use get_absolute_url(), which produces incorrectly formatted URLs.
      "createdAt": string (timestamp)
    }
  ]
  "citationNodes": [
    {
      "chatbotTextNode": {
      {
        "id": string (uuid)
        "charactersCount": integer
        "hitsCount": integer
        "text": string
        "updatedAt": string (timestamp)
        "filename": string
        "chatbotFile": object // Return complete ChatbotFile information, including the file URL and type, to support frontend image previews
        "knowledgeBaseFile": object // Same as get_chatbot_file, for backward compatibility
        "pageNumber": integer // Backward-compatible alias of ``page_start``.
        "pageStart": integer
        "pageEnd": integer
        "citationTitle": string // Source title shown on the inline citation hover card.
        "citationDescription": string // Source description shown on the inline citation hover card.
        "citationQuote": string // Quoted text snippet shown on the inline citation hover card.
        "hasImage": boolean // Determine whether an image is present (check the file type or Markdown images in text)
        "imageUrl": string // Extract the image URL (prefer the file URL; otherwise extract it from Markdown)
        "displayText": string // Return the full text with Markdown image markup removed
        "displayTitle": string // Return the display title (fallback: citation_title -> filename)
        "highlightedText": string // Return text with matched terms wrapped in ``<mark>`` tags.

Priority:
1. ES/OpenSearch native highlight (set by retrieve_api via ``_es_highlighted_text``)
2. Python regex fallback (keyword-based, works for all backends)
        "labels": [
          {
            "id": string (uuid)
            "name": string
          }
        ]
        "rawUserDefineMetadata"?: object // Metadata keys and values defined by the user. (Optional)
        "metadataEnabledForSearch": object // Map each user-defined metadata key on this node to whether it was sent into the LLM.

A key reaches the LLM only when its ``MetadataKey.enabled_for_search`` is True *and*
the node carries a value for it. Since this maps over ``raw_user_define_metadata``
(the keys the cited-documents block displays, all of which have a value),
``enabled_for_search`` alone decides the flag. The source of truth is the node's
knowledge base ``MetadataKey`` current setting, matching the AI Search indicator.
        "startCharIdx": integer
        "endCharIdx": integer
      }
      }
      "score"?: number (double) // Optional
      "displayScore": integer
      "citationNumber"?: integer // Citation number matching the [1], [2] markers in the reply content (Optional)
      "highlightedText"?: string // ES/OpenSearch highlight result with <mark> tags (Optional)
    }
  ]
  "canvas"?: { // Optional
  {
    "id": string (uuid)
    "name": string
    "canvasType": 
    {
    }
    "title": string
    "content": string
    "createdAt": string (timestamp)
  }
  }
  "slideTemplate"?:  // Optional
  {
    "slug": string
    "displayName": string
    "thumbnailUrls"?: [ // Optional
      string
    ]
  }
  "queryMetadata"?: { // Query metadata that controls the scope of searchable knowledge; see the description of each endpoint (Optional)
  {
    "knowledgeBases"?: [ // List of searchable knowledge bases; an empty array or null means none can be searched (no permission); omitting it imposes no restriction (Optional)
      {
        "knowledgeBaseId": string (uuid) // Knowledge base ID
        "chatbotFileIds"?: [ // List of file IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "knowledgeBaseFileIds"?: [ // List of file IDs (the new name for chatbotFileIds; this field takes precedence when both are provided) (Optional)
          string (uuid)
        ]
        "faqIds"?: [ // List of FAQ IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "hasUserSelectedAll"?: boolean // true selects all knowledge base content except the listed items; false selects only the listed items (Optional)
      }
    ]
    "labelRelations"?: { // Label filter criteria; an empty conditions array means no label filtering (not a lack of permission). Must be provided together with knowledgeBases; when provided alone, knowledge base retrieval does not apply label filtering (equivalent to no restriction) (Optional)
    {
      "operator": string (enum: AND, OR) // How to combine label conditions
      "conditions"?: [ // List of label conditions; supports nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "metadata"?: object // Message metadata, such as timezone and other environment information (Optional)
  "recordId": string // Return the ChatbotRecord ID corresponding to the Message

Note:
    For unsaved Messages (such as virtual Messages during streaming), return None directly
    to avoid triggering a DB query on the OneToOneField reverse relation (the N+1 problem).
  "broadcast"?: boolean // Optional
  "skipCopilotTrigger"?: boolean // Optional
  "activeRevisionNumber": integer
  "revisions": [
    {
      "id": string (uuid)
      "revisionNumber": integer
      "content": string
      "contentPayload": object
      "isActive": boolean
      "createdAt": string (timestamp)
    }
  ]
  "suggestedForId"?: string (uuid) // Optional
  "suggestionStatus": string
  "suggestionTriggerSource": string
  "suggestionError": string
  "retryCount": integer // How many times the LLM call was auto-retried during this message generation. Currently only Bedrock first-chunk stall triggers retry (max 1). Detailed per-attempt records live in metadata["retry_attempts"].
  "skillIds"?: [ // Per-message selected skill IDs from the WebChat client (camelCase: skillIds). (Optional)
    string (uuid)
  ]
  "toolIds"?: [ // Per-message selected tool IDs from the WebChat client (camelCase: toolIds). (Optional)
    string (uuid)
  ]
  "templateId"?: string (uuid) // Document/sheet template ID for file-level compilation (camelCase: templateId). (Optional)
  "videoGeneration": object // Return the latest GeneratedVideo state for FE reload recovery;
``errorMessage`` is blank to match the socket emit (raw value stays on the DB row).
  "isGreeting": boolean // Whether this message is a greeting message.
}
```

**Example Response Values**

```json
{
  "conversation": "c96a0fb9-d106-4b16-8706-3906533bafa2",
  "sender": {
    "id": 2.992461161002561e+38,
    "name": "jessiekuo",
    "avatar": "https://media-dev.maiagent.ai/static/images/default-user-avatar.png"
  },
  "type": "incoming",
  "content": "This is a test message",
  "contentPayload": {
    "content": "This is a test message",
    "items": [
      {
        "type": "text",
        "text": "This is a test message",
        "startTimestamp": null,
        "citations": []
      }
    ],
    "metadata": null
  },
  "feedback": null,
  "createdAt": "1748314926000",
  "attachments": [],
  "citations": [],
  "citationNodes": [],
  "canvas": {
    "id": "034eecd7-e0f4-46e9-88cb-4fefdb5b7ddd",
    "name": "system-architecture-diagram",
    "canvasType": "markdown",
    "title": "System Architecture Diagram",
    "content": "~~~mermaid\ngraph TD\n    A[User] --> B[Frontend Interface]\n    B --> C[API Layer]\n    C --> D[Database]\n~~~",
    "createdAt": "1748314926000"
  }
}
```

***

### Send an Outgoing Message <a href="#messages-outgoing-create" id="messages-outgoing-create"></a>

POST `/api/v1/messages/outgoing/`

#### Request Body

**Request Parameters**

| Field                                   | Type                                                         | Required | Description                                                                                                                                                                                                                                                      |
| --------------------------------------- | ------------------------------------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| conversation                            | string (uuid)                                                | Yes      |                                                                                                                                                                                                                                                                  |
| type                                    | string                                                       | No       |                                                                                                                                                                                                                                                                  |
| content                                 | string                                                       | No       |                                                                                                                                                                                                                                                                  |
| contentPayload                          | object                                                       | No       |                                                                                                                                                                                                                                                                  |
| attachments                             | array\[MessageAttachmentInput]                               | No       |                                                                                                                                                                                                                                                                  |
| canvas                                  | object                                                       | No       |                                                                                                                                                                                                                                                                  |
| canvas.name                             | string                                                       | Yes      |                                                                                                                                                                                                                                                                  |
| canvas.canvasType                       | object                                                       | Yes      |                                                                                                                                                                                                                                                                  |
| canvas.title                            | string                                                       | Yes      |                                                                                                                                                                                                                                                                  |
| canvas.content                          | string                                                       | Yes      |                                                                                                                                                                                                                                                                  |
| slideTemplate                           | object (with 3 properties: slug, displayName, thumbnailUrls) | No       |                                                                                                                                                                                                                                                                  |
| slideTemplate.slug                      | string                                                       | Yes      |                                                                                                                                                                                                                                                                  |
| slideTemplate.displayName               | string                                                       | Yes      |                                                                                                                                                                                                                                                                  |
| slideTemplate.thumbnailUrls             | array\[string]                                               | No       |                                                                                                                                                                                                                                                                  |
| queryMetadata                           | object                                                       | No       | Query metadata that controls the scope of searchable knowledge; see the description of each endpoint                                                                                                                                                             |
| queryMetadata.knowledgeBases            | array\[object]                                               | No       | List of searchable knowledge bases; an empty array or null means none can be searched (no permission); omitting it imposes no restriction                                                                                                                        |
| queryMetadata.labelRelations            | object                                                       | No       | Label filter criteria; an empty conditions array means no label filtering (not a lack of permission). Must be provided together with knowledgeBases; when provided alone, knowledge base retrieval does not apply label filtering (equivalent to no restriction) |
| queryMetadata.labelRelations.operator   | string (enum: AND, OR)                                       | Yes      | How to combine label conditions                                                                                                                                                                                                                                  |
| queryMetadata.labelRelations.conditions | array\[any]                                                  | No       | List of label conditions; supports nested operator/conditions combinations                                                                                                                                                                                       |
| metadata                                | object                                                       | No       | Message metadata, such as timezone and other environment information                                                                                                                                                                                             |
| broadcast                               | boolean                                                      | No       |                                                                                                                                                                                                                                                                  |
| skipCopilotTrigger                      | boolean                                                      | No       |                                                                                                                                                                                                                                                                  |
| suggestedForId                          | string (uuid)                                                | No       |                                                                                                                                                                                                                                                                  |
| skillIds                                | array\[string]                                               | No       | Per-message selected skill IDs from the WebChat client (camelCase: skillIds).                                                                                                                                                                                    |
| toolIds                                 | array\[string]                                               | No       | Per-message selected tool IDs from the WebChat client (camelCase: toolIds).                                                                                                                                                                                      |
| templateId                              | string (uuid)                                                | No       | Document/sheet template ID for file-level compilation (camelCase: templateId).                                                                                                                                                                                   |

**Example Request Schema**

```typescript
{
  "conversation": string (uuid)
  "type"?: string // Optional
  "content"?: string // Optional
  "contentPayload"?: object // Optional
  "attachments"?: [ // Optional
    {
      "id": string (uuid) // ID of an already-uploaded attachment, as returned by the attachment upload endpoint. Required: a missing, null or non-UUID id is rejected with HTTP 400 and the message is not created.
      "type"?:  // Optional
      {
      }
      "filename": string
      "file": string (uri)
      "conversation"?: string (uuid) // Optional
    }
  ]
  "canvas"?: { // Optional
  {
    "name": string
    "canvasType": 
    {
    }
    "title": string
    "content": string
  }
  }
  "slideTemplate"?:  // Optional
  {
    "slug": string
    "displayName": string
    "thumbnailUrls"?: [ // Optional
      string
    ]
  }
  "queryMetadata"?: { // Query metadata that controls the scope of searchable knowledge; see the description of each endpoint (Optional)
  {
    "knowledgeBases"?: [ // List of searchable knowledge bases; an empty array or null means none can be searched (no permission); omitting it imposes no restriction (Optional)
      {
        "knowledgeBaseId": string (uuid) // Knowledge base ID
        "chatbotFileIds"?: [ // List of file IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "knowledgeBaseFileIds"?: [ // List of file IDs (the new name for chatbotFileIds; this field takes precedence when both are provided) (Optional)
          string (uuid)
        ]
        "faqIds"?: [ // List of FAQ IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "hasUserSelectedAll"?: boolean // true selects all knowledge base content except the listed items; false selects only the listed items (Optional)
      }
    ]
    "labelRelations"?: { // Label filter criteria; an empty conditions array means no label filtering (not a lack of permission). Must be provided together with knowledgeBases; when provided alone, knowledge base retrieval does not apply label filtering (equivalent to no restriction) (Optional)
    {
      "operator": string (enum: AND, OR) // How to combine label conditions
      "conditions"?: [ // List of label conditions; supports nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "metadata"?: object // Message metadata, such as timezone and other environment information (Optional)
  "broadcast"?: boolean // Optional
  "skipCopilotTrigger"?: boolean // Optional
  "suggestedForId"?: string (uuid) // Optional
  "skillIds"?: [ // Per-message selected skill IDs from the WebChat client (camelCase: skillIds). (Optional)
    string (uuid)
  ]
  "toolIds"?: [ // Per-message selected tool IDs from the WebChat client (camelCase: toolIds). (Optional)
    string (uuid)
  ]
  "templateId"?: string (uuid) // Document/sheet template ID for file-level compilation (camelCase: templateId). (Optional)
}
```

**Example Request Values**

```json
{
  "conversation": "550e8400-e29b-41d4-a716-446655440000",
  "type": "Example string",
  "content": "Hello! I would like to learn about your products.",
  "contentPayload": null,
  "attachments": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "type": {},
      "filename": "document.pdf",
      "file": "https://example.com/file.jpg",
      "conversation": "550e8400-e29b-41d4-a716-446655440000"
    }
  ],
  "canvas": {
    "name": "Example name",
    "canvasType": {},
    "title": "Example name",
    "content": "Hello! I would like to learn about your products."
  },
  "slideTemplate": {
    "slug": "Example string",
    "displayName": "Example string",
    "thumbnailUrls": [
      "Example string"
    ]
  },
  "queryMetadata": {
    "knowledgeBases": [
      {
        "knowledgeBaseId": "550e8400-e29b-41d4-a716-446655440000",
        "chatbotFileIds": [
          "550e8400-e29b-41d4-a716-446655440000"
        ],
        "knowledgeBaseFileIds": [
          "550e8400-e29b-41d4-a716-446655440000"
        ],
        "faqIds": [
          "550e8400-e29b-41d4-a716-446655440000"
        ],
        "hasUserSelectedAll": true
      }
    ],
    "labelRelations": {
      "operator": "AND",
      "conditions": [
        null
      ]
    }
  },
  "metadata": null,
  "broadcast": true,
  "skipCopilotTrigger": true,
  "suggestedForId": "550e8400-e29b-41d4-a716-446655440000",
  "skillIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "toolIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "templateId": "550e8400-e29b-41d4-a716-446655440000"
}
```

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X POST "https://api.maiagent.ai/api/v1/messages/outgoing/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "conversation": "550e8400-e29b-41d4-a716-446655440000",
    "type": "Example string",
    "content": "Hello! I would like to learn about your products.",
    "contentPayload": null,
    "attachments": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "type": {},
        "filename": "document.pdf",
        "file": "https://example.com/file.jpg",
        "conversation": "550e8400-e29b-41d4-a716-446655440000"
      }
    ],
    "canvas": {
      "name": "Example name",
      "canvasType": {},
      "title": "Example name",
      "content": "Hello! I would like to learn about your products."
    },
    "slideTemplate": {
      "slug": "Example string",
      "displayName": "Example string",
      "thumbnailUrls": [
        "Example string"
      ]
    },
    "queryMetadata": {
      "knowledgeBases": [
        {
          "knowledgeBaseId": "550e8400-e29b-41d4-a716-446655440000",
          "chatbotFileIds": [
            "550e8400-e29b-41d4-a716-446655440000"
          ],
          "knowledgeBaseFileIds": [
            "550e8400-e29b-41d4-a716-446655440000"
          ],
          "faqIds": [
            "550e8400-e29b-41d4-a716-446655440000"
          ],
          "hasUserSelectedAll": true
        }
      ],
      "labelRelations": {
        "operator": "AND",
        "conditions": [
          null
        ]
      }
    },
    "metadata": null,
    "broadcast": true,
    "skipCopilotTrigger": true,
    "suggestedForId": "550e8400-e29b-41d4-a716-446655440000",
    "skillIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ],
    "toolIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ],
    "templateId": "550e8400-e29b-41d4-a716-446655440000"
  }'

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
};

// Request Body (payload)
const data = {
    "conversation": "550e8400-e29b-41d4-a716-446655440000",
    "type": "Example string",
    "content": "Hello! I would like to learn about your products.",
    "contentPayload": null,
    "attachments": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "type": {},
        "filename": "document.pdf",
        "file": "https://example.com/file.jpg",
        "conversation": "550e8400-e29b-41d4-a716-446655440000"
      }
    ],
    "canvas": {
      "name": "Example name",
      "canvasType": {},
      "title": "Example name",
      "content": "Hello! I would like to learn about your products."
    },
    "slideTemplate": {
      "slug": "Example string",
      "displayName": "Example string",
      "thumbnailUrls": [
        "Example string"
      ]
    },
    "queryMetadata": {
      "knowledgeBases": [
        {
          "knowledgeBaseId": "550e8400-e29b-41d4-a716-446655440000",
          "chatbotFileIds": [
            "550e8400-e29b-41d4-a716-446655440000"
          ],
          "knowledgeBaseFileIds": [
            "550e8400-e29b-41d4-a716-446655440000"
          ],
          "faqIds": [
            "550e8400-e29b-41d4-a716-446655440000"
          ],
          "hasUserSelectedAll": true
        }
      ],
      "labelRelations": {
        "operator": "AND",
        "conditions": [
          null
        ]
      }
    },
    "metadata": null,
    "broadcast": true,
    "skipCopilotTrigger": true,
    "suggestedForId": "550e8400-e29b-41d4-a716-446655440000",
    "skillIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ],
    "toolIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ],
    "templateId": "550e8400-e29b-41d4-a716-446655440000"
  };

axios.post("https://api.maiagent.ai/api/v1/messages/outgoing/", data, config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/messages/outgoing/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request Body (payload)
data = {
    "conversation": "550e8400-e29b-41d4-a716-446655440000",
    "type": "Example string",
    "content": "Hello! I would like to learn about your products.",
    "contentPayload": None,
    "attachments": [
        {
            "id": "550e8400-e29b-41d4-a716-446655440000",
            "type": {},
            "filename": "document.pdf",
            "file": "https://example.com/file.jpg",
            "conversation": "550e8400-e29b-41d4-a716-446655440000"
        }
    ],
    "canvas": {
        "name": "Example name",
        "canvasType": {},
        "title": "Example name",
        "content": "Hello! I would like to learn about your products."
    },
    "slideTemplate": {
        "slug": "Example string",
        "displayName": "Example string",
        "thumbnailUrls": [
            "Example string"
        ]
    },
    "queryMetadata": {
        "knowledgeBases": [
            {
                "knowledgeBaseId": "550e8400-e29b-41d4-a716-446655440000",
                "chatbotFileIds": [
                    "550e8400-e29b-41d4-a716-446655440000"
                ],
                "knowledgeBaseFileIds": [
                    "550e8400-e29b-41d4-a716-446655440000"
                ],
                "faqIds": [
                    "550e8400-e29b-41d4-a716-446655440000"
                ],
                "hasUserSelectedAll": True
            }
        ],
        "labelRelations": {
            "operator": "AND",
            "conditions": [
                None
            ]
        }
    },
    "metadata": None,
    "broadcast": True,
    "skipCopilotTrigger": True,
    "suggestedForId": "550e8400-e29b-41d4-a716-446655440000",
    "skillIds": [
        "550e8400-e29b-41d4-a716-446655440000"
    ],
    "toolIds": [
        "550e8400-e29b-41d4-a716-446655440000"
    ],
    "templateId": "550e8400-e29b-41d4-a716-446655440000"
}

response = requests.post(url, json=data, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request failed:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->post("https://api.maiagent.ai/api/v1/messages/outgoing/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'conversation' => '550e8400-e29b-41d4-a716-446655440000',
            'type' => 'Example string',
            'content' => 'Hello! I would like to learn about the product.',
            'contentPayload' => null,
            'attachments' => [
                [
                    'id' => '550e8400-e29b-41d4-a716-446655440000',
                    'type' => new \stdClass(),
                    'filename' => 'document.pdf',
                    'file' => 'https://example.com/file.jpg',
                    'conversation' => '550e8400-e29b-41d4-a716-446655440000'
                ]
            ],
            'canvas' => [
                'name' => 'Example name',
                'canvasType' => new \stdClass(),
                'title' => 'Example name',
                'content' => 'Hello! I would like to learn about the product.'
            ],
            'slideTemplate' => [
                'slug' => 'Example string',
                'displayName' => 'Example string',
                'thumbnailUrls' => [
                    'Example string'
                ]
            ],
            'queryMetadata' => [
                'knowledgeBases' => [
                    [
                        'knowledgeBaseId' => '550e8400-e29b-41d4-a716-446655440000',
                        'chatbotFileIds' => [
                            '550e8400-e29b-41d4-a716-446655440000'
                        ],
                        'knowledgeBaseFileIds' => [
                            '550e8400-e29b-41d4-a716-446655440000'
                        ],
                        'faqIds' => [
                            '550e8400-e29b-41d4-a716-446655440000'
                        ],
                        'hasUserSelectedAll' => true
                    ]
                ],
                'labelRelations' => [
                    'operator' => 'AND',
                    'conditions' => [
                        null
                    ]
                ]
            ],
            'metadata' => null,
            'broadcast' => true,
            'skipCopilotTrigger' => true,
            'suggestedForId' => '550e8400-e29b-41d4-a716-446655440000',
            'skillIds' => [
                '550e8400-e29b-41d4-a716-446655440000'
            ],
            'toolIds' => [
                '550e8400-e29b-41d4-a716-446655440000'
            ],
            'templateId' => '550e8400-e29b-41d4-a716-446655440000'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request failed: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 201**

**Example Response Schema**

```typescript
{
  "id": string (uuid)
  "conversation": string (uuid)
  "sender": 
  {
    "id": string (uuid)
    "name": string
    "avatar": string
    "email"?: string (email) // Optional
    "phoneNumber"?: string // Optional
  }
  "type"?: string // Optional
  "content"?: string // Optional
  "contentPayload"?: object // Optional
  "feedback": 
  {
    "id": string (uuid)
    "type": 
    {
    }
    "suggestion"?: string // Optional
    "updatedAt": string (timestamp)
  }
  "createdAt": string (timestamp)
  "attachments"?: [ // Optional
    {
      "id": string (uuid) // ID of an already-uploaded attachment, as returned by the attachment upload endpoint. Required: a missing, null or non-UUID id is rejected with HTTP 400 and the message is not created.
      "type"?:  // Optional
      {
      }
      "filename": string
      "file": string (uri)
      "expiresAt": string // ISO datetime when this attachment will be auto-deleted.

For conversation-bound attachments: conversation.last_message_created_at + retention_days.
For attachments without a conversation: created_at + retention_days.
Returns None when cleanup is disabled (effective_from not set).
      "conversation"?: string (uuid) // Optional
    }
  ]
  "citations": [
    {
      "id": string (uuid)
      "filename": string // File name
      "file": string (uri) // File to upload
      "fileType": string
      "knowledgeBase"?:  // Optional
      {
        "id": string (uuid)
        "name": string
      }
      "size": integer // File size in bytes
      "status": 
      {
      }
      "parser": 
      {
        "id": string (uuid)
        "name": string
        "provider": 
        {
        }
        "isTimestampSttProvider": boolean
      }
      "labels"?: [ // Optional
        {
          "id": string (uuid)
          "name": string
        }
      ]
      "rawUserDefineMetadata"?: object // Optional
      "speakerLabels": [
        {
          "id": string (uuid)
          "originalLabel": string // Original speaker label from diarization (e.g., SPEAKER_00)
          "customName"?: string // User-defined speaker name (e.g., John) (Optional)
          "displayName": string
        }
      ]
      "vectorStorageSize": integer // Size of vectors for this file in Elasticsearch (bytes)
      "chunksCount": integer // Number of chunks/nodes generated from this file
      "waitingTime": number (double)
      "processingTime": number (double)
      "processingTimeDetails": object
      "ocrStatus": string // OCR result for scanned pages: done, skipped, partial, off; null before parsing or for legacy files
      "ocrSkippedReason": string // Reason OCR was not performed on scanned pages: no_azure_settings, azure_unavailable, insufficient_credits, transient_error, time_budget, disabled; null when OCR is completed
      "previewUrl": string // For presentations (pptx/ppt), returns the URL of a previewable PDF derivative (displayed in the frontend PDF viewer); otherwise None.

Generate the URL with the same CustomizedFileFieldSerializer as the `file` field to ensure a consistent format (including each
storage provider's presign/host rules). Do not use get_absolute_url(): it produces incorrectly formatted URLs.
      "createdAt": string (timestamp)
    }
  ]
  "citationNodes": [
    {
      "chatbotTextNode": {
      {
        "id": string (uuid)
        "charactersCount": integer
        "hitsCount": integer
        "text": string
        "updatedAt": string (timestamp)
        "filename": string
        "chatbotFile": object // Return full ChatbotFile details, including the file URL and type, to support image previews in the frontend
        "knowledgeBaseFile": object // Same as get_chatbot_file, provided for backward compatibility
        "pageNumber": integer // Backward-compatible alias of ``page_start``.
        "pageStart": integer
        "pageEnd": integer
        "citationTitle": string // Source title shown on the inline citation hover card.
        "citationDescription": string // Source description shown on the inline citation hover card.
        "citationQuote": string // Quoted text snippet shown on the inline citation hover card.
        "hasImage": boolean // Check whether images are present (by checking the file type or Markdown images in text)
        "imageUrl": string // Extract the image URL (prefer the file URL; otherwise extract it from Markdown)
        "displayText": string // Return the full text with image Markdown markup removed
        "displayTitle": string // Return the display title (fallback: citation_title -> filename)
        "highlightedText": string // Return text with matched terms wrapped in ``<mark>`` tags.

Priority:
1. ES/OpenSearch native highlight (set by retrieve_api via ``_es_highlighted_text``)
2. Python regex fallback (keyword-based, works for all backends)
        "labels": [
          {
            "id": string (uuid)
            "name": string
          }
        ]
        "rawUserDefineMetadata"?: object // Metadata keys and values defined by the user. (Optional)
        "metadataEnabledForSearch": object // Map each user-defined metadata key on this node to whether it was sent into the LLM.

A key reaches the LLM only when its ``MetadataKey.enabled_for_search`` is True *and*
the node carries a value for it. Since this maps over ``raw_user_define_metadata``
(the keys the cited-documents block displays, all of which have a value),
``enabled_for_search`` alone decides the flag. The source of truth is the node's
knowledge base ``MetadataKey`` current setting, matching the AI Search indicator.
        "startCharIdx": integer
        "endCharIdx": integer
      }
      }
      "score"?: number (double) // Optional
      "displayScore": integer
      "citationNumber"?: integer // Citation number matching the [1], [2] markers in the reply content (Optional)
      "highlightedText"?: string // ES/OpenSearch highlight result with <mark> tags (Optional)
    }
  ]
  "canvas"?: { // Optional
  {
    "id": string (uuid)
    "name": string
    "canvasType": 
    {
    }
    "title": string
    "content": string
    "createdAt": string (timestamp)
  }
  }
  "slideTemplate"?:  // Optional
  {
    "slug": string
    "displayName": string
    "thumbnailUrls"?: [ // Optional
      string
    ]
  }
  "queryMetadata"?: { // Query metadata controlling the scope of searchable knowledge; see each endpoint for details (Optional)
  {
    "knowledgeBases"?: [ // List of searchable knowledge bases; an empty array or null means none are searchable (no permission); omission means no restriction (Optional)
      {
        "knowledgeBaseId": string (uuid) // Knowledge base ID
        "chatbotFileIds"?: [ // List of file IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "knowledgeBaseFileIds"?: [ // List of file IDs (the new name for chatbotFileIds; this field takes precedence when both are provided) (Optional)
          string (uuid)
        ]
        "faqIds"?: [ // List of FAQ IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "hasUserSelectedAll"?: boolean // true selects all knowledge base content except the listed items; false selects only the listed items (Optional)
      }
    ]
    "labelRelations"?: { // Label filter criteria; an empty conditions array means no label filtering (not a lack of permission). Must be provided together with knowledgeBases; when provided alone, label filtering is not applied to knowledge base retrieval (equivalent to no restriction) (Optional)
    {
      "operator": string (enum: AND, OR) // How label conditions are combined
      "conditions"?: [ // List of label conditions; supports nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "metadata"?: object // Message metadata, such as timezone and other environment information (Optional)
  "recordId": string // Return the ChatbotRecord ID associated with the Message

Note:
    For an unsaved Message (such as a virtual Message during streaming), return None directly,
    avoiding a database query on the OneToOneField reverse relation (the N+1 problem).
  "broadcast"?: boolean // Optional
  "skipCopilotTrigger"?: boolean // Optional
  "activeRevisionNumber": integer
  "revisions": [
    {
      "id": string (uuid)
      "revisionNumber": integer
      "content": string
      "contentPayload": object
      "isActive": boolean
      "createdAt": string (timestamp)
    }
  ]
  "suggestedForId"?: string (uuid) // Optional
  "suggestionStatus": string
  "suggestionTriggerSource": string
  "suggestionError": string
  "retryCount": integer // How many times the LLM call was auto-retried during this message generation. Currently only Bedrock first-chunk stall triggers retry (max 1). Detailed per-attempt records live in metadata["retry_attempts"].
  "skillIds"?: [ // Per-message selected skill IDs from the WebChat client (camelCase: skillIds). (Optional)
    string (uuid)
  ]
  "toolIds"?: [ // Per-message selected tool IDs from the WebChat client (camelCase: toolIds). (Optional)
    string (uuid)
  ]
  "templateId"?: string (uuid) // Document/sheet template ID for file-level compilation (camelCase: templateId). (Optional)
  "videoGeneration": object // Return the latest GeneratedVideo state for FE reload recovery;
``errorMessage`` is blank to match the socket emit (raw value stays on the DB row).
  "isGreeting": boolean // Whether this message is a greeting message.
}
```

**Example Response Value**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "conversation": "550e8400-e29b-41d4-a716-446655440000",
  "sender": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "avatar": "Response string",
    "email": "response@example.com",
    "phoneNumber": "Response string"
  },
  "type": "Response string",
  "content": "Response string",
  "contentPayload": null,
  "feedback": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": {},
    "suggestion": "Response string",
    "updatedAt": "Response string"
  },
  "createdAt": "Response string",
  "attachments": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "type": {},
      "filename": "Response string",
      "file": "https://example.com/file.jpg",
      "expiresAt": "Response string",
      "conversation": "550e8400-e29b-41d4-a716-446655440000"
    }
  ],
  "citations": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "filename": "Response string",
      "file": "https://example.com/file.jpg",
      "fileType": "Response string",
      "knowledgeBase": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string"
      },
      "size": 456,
      "status": {},
      "parser": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string",
        "provider": {},
        "isTimestampSttProvider": false
      },
      "labels": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "name": "Response string"
        }
      ],
      "rawUserDefineMetadata": null,
      "speakerLabels": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "originalLabel": "Response string",
          "customName": "Response string",
          "displayName": "Response string"
        }
      ],
      "vectorStorageSize": 456,
      "chunksCount": 456,
      "waitingTime": 456,
      "processingTime": 456,
      "processingTimeDetails": null,
      "ocrStatus": "Response string",
      "ocrSkippedReason": "Response string",
      "previewUrl": "Response string",
      "createdAt": "Response string"
    }
  ],
  "citationNodes": [
    {
      "chatbotTextNode": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "charactersCount": 456,
        "hitsCount": 456,
        "text": "Response string",
        "updatedAt": "Response string",
        "filename": "Response string",
        "chatbotFile": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "name": "Example response name",
          "description": "Example response description"
        },
        "knowledgeBaseFile": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "name": "Example response name",
          "description": "Example response description"
        },
        "pageNumber": 456,
        "pageStart": 456,
        "pageEnd": 456,
        "citationTitle": "Response string",
        "citationDescription": "Response string",
        "citationQuote": "Response string",
        "hasImage": false,
        "imageUrl": "Response string",
        "displayText": "Response string",
        "displayTitle": "Response string",
        "highlightedText": "Response string",
        "labels": [
          {
            "id": "550e8400-e29b-41d4-a716-446655440000",
            "name": "Response string"
          }
        ],
        "rawUserDefineMetadata": null,
        "metadataEnabledForSearch": {
          "key1": "value1",
          "key2": "value2",
          "createdAt": "2025-01-01T00:00:00.000Z"
        },
        "startCharIdx": 456,
        "endCharIdx": 456
      },
      "score": 456,
      "displayScore": 456,
      "citationNumber": 456,
      "highlightedText": "Response string"
    }
  ],
  "canvas": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "canvasType": {},
    "title": "Response string",
    "content": "Response string",
    "createdAt": "Response string"
  },
  "slideTemplate": {
    "slug": "Response string",
    "displayName": "Response string",
    "thumbnailUrls": [
      "Response string"
    ]
  },
  "queryMetadata": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "metadata": null,
  "recordId": "Response string",
  "broadcast": false,
  "skipCopilotTrigger": false,
  "activeRevisionNumber": 456,
  "revisions": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "revisionNumber": 456,
      "content": "Response string",
      "contentPayload": null,
      "isActive": false,
      "createdAt": "Response string"
    }
  ],
  "suggestedForId": "550e8400-e29b-41d4-a716-446655440000",
  "suggestionStatus": "Response string",
  "suggestionTriggerSource": "Response string",
  "suggestionError": "Response string",
  "retryCount": 456,
  "skillIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "toolIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "templateId": "550e8400-e29b-41d4-a716-446655440000",
  "videoGeneration": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "isGreeting": false
}
```

***

### Create a New Conversation <a href="#conversations-create" id="conversations-create"></a>

POST `/api/v1/conversations/`

#### Request Body

**Request Parameters**

| Field            | Type          | Required | Description |
| ---------------- | ------------- | -------- | ----------- |
| webChat          | string (uuid) | Yes      |             |
| contact          | string (uuid) | No       |             |
| clientPlatform   | object        | No       |             |
| workingDirectory | string        | No       |             |

**Example Request Schema**

```typescript
{
  "webChat": string (uuid)
  "contact"?: string (uuid) // Optional
  "clientPlatform"?:  // Optional
  {
  }
  "workingDirectory"?: string // Optional
}
```

**Example Request Value**

```json
{
  "webChat": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
```

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X POST "https://api.maiagent.ai/api/v1/conversations/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "webChat": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  }'

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
};

// Request Body (payload)
const data = {
    "webChat": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  };

axios.post("https://api.maiagent.ai/api/v1/conversations/", data, config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request failed:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/conversations/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request Body (payload)
data = {
    "webChat": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}

response = requests.post(url, json=data, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request failed:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->post("https://api.maiagent.ai/api/v1/conversations/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'webChat' => '3fa85f64-5717-4562-b3fc-2c963f66afa6'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request failed: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 201**

**Example Response Schema**

```typescript
{
  "id": string (uuid)
  "contact": { // Lightweight Contact Serializer for the Conversation List view

Includes only the fields required for the List view, omitting inboxes, mcp_credentials, and api_credentials
to avoid N+1 queries. email / phone_number / metadata are scalar fields on Contact itself,
needed for display in the frontend conversation sidebar, with no N+1 cost.
  {
    "id": string (uuid)
    "name": string
    "avatar"?: string // Stores our own media as a relative storage key, or an external CDN URL as-is. (Optional)
    "email"?: // May have different types (Optional)
    string (email) // Optional
    "phoneNumber"?: string // Optional
    "metadata"?: object // Custom customer information shown in the customer panel of the conversation page (Optional)
    "sourceId"?: string // Optional
    "tags": [
      {
        "id": string (uuid)
        "name": string
      }
    ]
  }
  }
  "inbox": { // Inbox summary embedded in a conversation payload.

``unread_conversations_count`` stays here because it is part of the
documented REST conversation API. It is an org-wide denormalized
counter, so WS emit paths must run the payload through
:func:`strip_org_wide_unread_count` before broadcasting it to rooms
that include members without ``GroupInbox.can_view_conversations``.
  {
    "id": string (uuid)
    "name": string
    "channelType": string (enum: line, telegram, teams, web, messenger, instagram, email, whatsapp, slack, team_plus, line_works, viber) // * `line` - LINE
* `telegram` - Telegram
* `teams` - Teams
* `web` - Web
* `messenger` - Messenger
* `instagram` - Instagram
* `email` - Email
* `whatsapp` - WhatsApp
* `slack` - Slack
* `team_plus` - Team+
* `line_works` - LINE WORKS
* `viber` - Viber
    "unreadConversationsCount"?: integer // Optional
    "signAuth"?:  // Optional
    {
      "id": string (uuid)
      "signSource": string (uuid)
      "signParams": {
      {
        "keycloak": 
        {
          "clientId": string
          "url": string
          "realm": string
        }
        "line": 
        {
          "liffId": string
        }
        "ad": 
        {
          "saml": string
        }
        "google"?:  // Optional
        {
          "enabled": boolean
        }
      }
      }
      "sourceIdAccessEnabled"?: boolean // When enabled, embedded visitors presenting a Source ID (verified per the verify mode below) may chat without going through the login flow. Disabled keeps every existing behavior unchanged. (Optional)
      "sourceIdVerifyMode"?:  // "Require signature verification": the embedding site must send an HMAC signature computed on its backend. "Source ID only": anyone knowing a Source ID can act as that contact; suitable only for testing or closed networks.

* `signature` - Require signature verification
* `raw` - Source ID only (Optional)
      {
      }
      "sourceIdSignatureTtlMinutes"?:  // How long a signature can be used to obtain the contact identity. Not a session lifetime: once exchanged, the session follows the existing WebChat mechanism.

* `1` - 1
* `5` - 5
* `15` - 15 (Optional)
      {
      }
      "keycloakTokenPassthrough"?: boolean // When enabled, the Keycloak access token a visitor logged in with is forwarded as the Authorization header when the AI calls MCP and API tools on their behalf, so tools do not require a separate per-tool login. Only for the Keycloak sign source. Never overrides an Authorization header a tool already resolves from its own credentials. The visitor identity is the organization member bound to the conversation at login, never the contact Source ID. (Optional)
      "keycloakTokenPassthroughAllowedHosts"?: [ // Hostnames (or "*.example.com" wildcards) of the tools the visitor Keycloak token may be sent to (Optional)
        string
      ]
      "organizationLogin": object // How visitors sign in when the sign source is "organization" (mirrors the back-office login); null for other sign sources
    }
    "enableAnalysis"?: boolean // Optional
  }
  }
  "chatbot": object // Chatbot (Agent) summary from the conversation's inbox, already eager-loaded.

``is_maigpt`` lets the client tell the default MaiGPT agent apart (icon, feature
gate) without comparing against the organization's ``maigpt_inbox`` id;
``file_workspace_mode`` is the effective P6 mode (MaiGPT, blueprint and the
organization's file-library switch already applied).
  "title": string
  "lastMessage"?: { // Optional
  {
    "id": string (uuid)
    "type"?: string // Optional
    "content"?: string // Optional
    "createdAt": string (timestamp)
  }
  }
  "callRecord": 
  {
    "id": string (uuid)
    "status"?: string (enum: initiated, ringing, in-progress, completed, failed, busy, no-answer, canceled) // * `initiated` - Initiated
* `ringing` - Ringing
* `in-progress` - In Progress
* `completed` - Completed
* `failed` - Failed
* `busy` - Busy
* `no-answer` - No Answer
* `canceled` - Canceled (Optional)
    "direction": string (enum: inbound, outbound) // * `inbound` - Inbound
* `outbound` - Outbound
    "isActive": boolean
    "canHangup": boolean
  }
  "lastMessageCreatedAt": string (timestamp)
  "unreadMessagesCount": integer
  "autoReplyEnabled": boolean
  "isAutoReplyNow": boolean
  "willAiReplyNow": boolean // Whether a text message arriving through the inbox gets an AI reply right now.

The messaging channels are gated by ``is_auto_reply_now`` (auto reply on and inside the
inbox auto-reply schedule) and by the inbox having a chatbot. The chatbot's API web chat
is answered by the completions endpoint, which ignores the schedule, so there the
conversation switch alone decides. The channel is only looked up for a web inbox outside
its schedule.

Surfaces that ignore the schedule on any inbox answer from their own rule instead: the
completions endpoint continuing an existing conversation (``completions_progress_status``)
and voice turns, which the voice agent answers.
  "lastReadAt": string (timestamp)
  "createdAt": string (timestamp)
  "isGroupChat": boolean
  "enableGroupMention"?: boolean // Optional
  "queryMetadata"?: { // Query metadata that controls the searchable knowledge scope; see each endpoint for details (Optional)
  {
    "knowledgeBases"?: [ // List of searchable knowledge bases; an empty array or null denies access to all knowledge bases (no permission); omission imposes no restriction (Optional)
      {
        "knowledgeBaseId": string (uuid) // Knowledge base ID
        "chatbotFileIds"?: [ // List of file IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "knowledgeBaseFileIds"?: [ // List of file IDs (the new name for chatbotFileIds; this field takes precedence when both are provided) (Optional)
          string (uuid)
        ]
        "faqIds"?: [ // List of FAQ IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "hasUserSelectedAll"?: boolean // true selects all knowledge base content except the listed items; false selects only the listed items (Optional)
      }
    ]
    "labelRelations"?: { // Label filter criteria; an empty conditions array applies no label filter (it does not deny access). Must be provided together with knowledgeBases; when supplied alone, knowledge base retrieval does not apply label filtering (no restriction) (Optional)
    {
      "operator": string (enum: AND, OR) // How label conditions are combined
      "conditions"?: [ // List of label conditions; supports nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "toolMask"?: object // Enabled/disabled state per tool: {tool_id: bool} (Optional)
  "connectorMask"?: object // Enabled/disabled state per connector: {connector_id: bool} (Optional)
  "thinkingConfig"?: object // None = not set (inherit the chatbot setting); otherwise {"reasoning_effort": <level>} (Optional)
  "thinkingConfigSchema": { // Thinking configuration form for this model; null means the model has no thinking configuration
  {
    "type"?: string // Form type; always reasoning_effort (Optional)
    "fields"?: object // reasoning_effort field: options lists the thinking levels accepted by this model (from lowest to highest); default is always null (Optional)
    "canDisable"?: boolean // Whether thinking can be disabled for this model; when true, options includes off (Optional)
    "unsetBehavior"?: string (enum: off, model_default) // Actual behavior when neither the conversation nor the assistant specifies a thinking level: off disables thinking (fastest); model_default lets the model decide or enables thinking at the platform default level (Optional)
    "unsetLevel"?: string // Level actually sent by the platform when no level is configured; null omits the thinking parameter and uses the model default (Optional)
  }
  }
  "effectiveThinkingConfig": object // The level the next reply sends on this conversation's model, after gating and clamping.

The stored choice resolves conversation > chatbot > the LLM row's default level; None means
the model sends no thinking parameter, so the provider default applies.
  "llm": string (uuid) // The model the next reply runs on: the conversation's pin, else its assistant's current model.

TC-0205: returning the stored ``conversation.llm`` would show a model an unpinned conversation no
longer uses once its assistant switches model, while ``thinking_config_schema`` already follows the
effective model. Read-only: displaying it never marks the conversation as explicitly selected;
only the update-llm endpoint does.
  "deepResearchStatus":  // Status of deep research for this conversation

* `not_used` - Not Used
* `started` - Started
* `running` - Running
* `completed` - Completed
* `failed` - Failed
  {
  }
  "deepResearchOutputFormat": // May have different types
  string (enum: canvas, chat, file) // Chosen delivery form for the finished report. Set only when the user clicks a format in the pre-research choice card; reset to NULL each time Deep Research is (re-)armed. NULL means no explicit choice (or a plan-skipping request) and falls back to Canvas at render time, but stays distinct from an explicit canvas pick so the frontend can lock the card only after a real selection. To roll back to fixed-Canvas behavior, force this to NULL/canvas — no redeploy needed.

* `canvas` - Canvas
* `chat` - Chat reply
* `file` - Downloadable file
  "ipAddress": string
  "ipCountryCode": string
  "ipRecordedAt": string (timestamp)
  "assignee": 
  {
    "id": string (uuid)
    "name": string
    "userId": string (uuid)
  }
  "status"?: string (enum: open, resolved, queued) // * `open` - Open
* `resolved` - Resolved
* `queued` - Queued (Optional)
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "memberIds"?: [ // Optional
        string (uuid)
      ]
    }
  ]
  "progressStatus": string
  "firstResponseAt"?: string (timestamp) // Optional
  "queuedAt"?: string (timestamp) // Optional
  "assignedAt"?: string (timestamp) // Optional
  "transferReason"?: string // Optional
  "satisfactionScore": integer // Conversation satisfaction rating on a 5-point scale (1=very dissatisfied, 5=very satisfied)
  "satisfactionComment": string
  "satisfactionSubmittedAt": string (timestamp)
  "latestAnalysisSummary": string // The conversation's current analysis summary (its active summary revision).

Reads the `latest_analysis_summary` annotation added by `setup_eager_loading`
to avoid an N+1 query. All read paths (list / retrieve / create) eager-load,
so the annotation is present; it falls back to '' only for instances built
outside `setup_eager_loading`.
  "clientPlatform":  // Client platform that created this conversation (web, desktop)

* `web` - Web
* `desktop` - Desktop
  {
  }
  "workingDirectory": string // Absolute path of the local folder bound to a desktop conversation. Only set for desktop conversations; null for web conversations.
  "slideProjectId": string
  "matchedField": string // Cached per instance: ``get_matched_message_id`` / ``get_matched_snippet``
both read this, so resolving it once avoids recomputing the keyword and
title-match logic three times per serialized conversation.
  "matchedSnippet": string // Keyword-centred excerpt of the matched message, with ellipses.

Reads the ``matched_message_content`` annotation added by
``ConversationViewSet._annotate_keyword_match``; no extra query.
  "matchedMessageId": string
  "pinnedAt": string (timestamp) // When the conversation was pinned. Null means unpinned.
  "folder": string (uuid) // The user-defined sidebar folder this conversation is grouped into. Null means unclassified (shown under the time groups). Deleting the folder resets this to null without deleting the conversation.
  "hasActiveBrowserSession": boolean // Whether a browser_use session snapshot exists to resume.

Lets the frontend know it should proactively re-request the browser
panel (emit ``browser:start``) after a page reload, instead of only
ever showing the panel when a live ``browser:frame`` push happens to
arrive first.
  "callerOrganizationId": string // Bound caller organization (stamped at conversation start); null when unbound
  "handoff": object // Handoff information: enabled is the platform switch; the continuation conversation includes source_conversation_id/source_title; the original conversation being handed off includes continued_to_conversation_id/continued_to_title and latest_summary_message_id
  "displayContextUsage": boolean // Whether to show the context usage percentage bar (a conversation platform switch; always true for MaiGPT; independent of the handoff switch)
  "autoSplitReason": string // Reason this conversation was created by automatic conversation splitting: resolved (after resolution) / idle (inactivity); null if not created by splitting
  "continuedFrom": object // Conversation continued by this split conversation: {id, last_message_at, deleted}; if the old conversation was deleted, id=null and deleted=true
  "continuedTo": object // Most recent conversation split from this conversation: {id, created_at}; null if none
  "continuationSummary": object // Continuation summary: {state: disabled|pending|ready|not_applied, items: [{conversation_id, content, generated_at, last_message_at, old_status}]} (items ordered newest first); null if not created by splitting
  "conversationKind": string // chat = a chat with an agent; secretary = the member's personal secretary Work chat
}
```

**Example response values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "contact": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "response string",
    "avatar": "response string",
    "email": "response@example.com",
    "phoneNumber": "response string",
    "metadata": null,
    "sourceId": "response string",
    "tags": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "response string"
      }
    ]
  },
  "inbox": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "response string",
    "channelType": "line",
    "unreadConversationsCount": 456,
    "signAuth": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "signSource": "550e8400-e29b-41d4-a716-446655440000",
      "signParams": {
        "keycloak": {
          "clientId": "response string",
          "url": "response string",
          "realm": "response string"
        },
        "line": {
          "liffId": "response string"
        },
        "ad": {
          "saml": "response string"
        },
        "google": {
          "enabled": false
        }
      },
      "sourceIdAccessEnabled": false,
      "sourceIdVerifyMode": {},
      "sourceIdSignatureTtlMinutes": {},
      "keycloakTokenPassthrough": false,
      "keycloakTokenPassthroughAllowedHosts": [
        "response string"
      ],
      "organizationLogin": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      }
    },
    "enableAnalysis": false
  },
  "chatbot": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "title": "response string",
  "lastMessage": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "response string",
    "content": "response string",
    "createdAt": "response string"
  },
  "callRecord": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "initiated",
    "direction": "inbound",
    "isActive": false,
    "canHangup": false
  },
  "lastMessageCreatedAt": "response string",
  "unreadMessagesCount": 456,
  "autoReplyEnabled": false,
  "isAutoReplyNow": false,
  "willAiReplyNow": false,
  "lastReadAt": "response string",
  "createdAt": "response string",
  "isGroupChat": false,
  "enableGroupMention": false,
  "queryMetadata": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "toolMask": null,
  "connectorMask": null,
  "thinkingConfig": null,
  "thinkingConfigSchema": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "effectiveThinkingConfig": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "llm": "550e8400-e29b-41d4-a716-446655440000",
  "deepResearchStatus": {},
  "deepResearchOutputFormat": "canvas",
  "ipAddress": "response string",
  "ipCountryCode": "response string",
  "ipRecordedAt": "response string",
  "assignee": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "response string",
    "userId": "550e8400-e29b-41d4-a716-446655440000"
  },
  "status": "open",
  "tags": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "response string",
      "memberIds": [
        "550e8400-e29b-41d4-a716-446655440000"
      ]
    }
  ],
  "progressStatus": "response string",
  "firstResponseAt": "response string",
  "queuedAt": "response string",
  "assignedAt": "response string",
  "transferReason": "response string",
  "satisfactionScore": 456,
  "satisfactionComment": "response string",
  "satisfactionSubmittedAt": "response string",
  "latestAnalysisSummary": "response string",
  "clientPlatform": {},
  "workingDirectory": "response string",
  "slideProjectId": "response string",
  "matchedField": "response string",
  "matchedSnippet": "response string",
  "matchedMessageId": "response string",
  "pinnedAt": "response string",
  "folder": "550e8400-e29b-41d4-a716-446655440000",
  "hasActiveBrowserSession": false,
  "callerOrganizationId": "response string",
  "handoff": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "displayContextUsage": false,
  "autoSplitReason": "response string",
  "continuedFrom": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "continuedTo": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "continuationSummary": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "conversationKind": "response string"
}
```

**Status code: 4XX - Error response; X-Error-Code carries its code (see "Error codes").**

***

### List Messages <a href="#messages-list" id="messages-list"></a>

GET `/api/v1/messages/`

#### Parameters

| Parameter name | Required | Type    | Description                           |
| -------------- | -------- | ------- | ------------------------------------- |
| `conversation` | ✅        | string  | Conversation ID                       |
| `cursor`       | ❌        | string  | The pagination cursor value.          |
| `pageSize`     | ❌        | integer | Number of results to return per page. |

#### Code examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X GET "https://api.maiagent.ai/api/v1/messages/?conversation=example&cursor=example&pageSize=1" \
  -H "Authorization: Api-Key YOUR_API_KEY"

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY'
  }
};

axios.get("https://api.maiagent.ai/api/v1/messages/?conversation=example&cursor=example&pageSize=1", config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request failed:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/messages/?conversation=example&cursor=example&pageSize=1"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY"
}


response = requests.get(url, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request failed:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->get("https://api.maiagent.ai/api/v1/messages/?conversation=example&cursor=example&pageSize=1", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request failed: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response

**Status code: 200**

**Example response schema**

```typescript
{
  "count": integer
  "next"?: string (uri) // Optional
  "previous"?: string (uri) // Optional
  "results": [
    {
      "id": string (uuid)
      "conversation": string (uuid)
      "sender": 
      {
        "id": string (uuid)
        "name": string
        "avatar": string
        "email"?: string (email) // Optional
        "phoneNumber"?: string // Optional
      }
      "type"?: string // Optional
      "content"?: string // Optional
      "contentPayload"?: object // Optional
      "feedback": 
      {
        "id": string (uuid)
        "type": 
        {
        }
        "suggestion"?: string // Optional
        "updatedAt": string (timestamp)
      }
      "createdAt": string (timestamp)
      "attachments"?: [ // Optional
        {
          "id": string (uuid) // ID of an already-uploaded attachment, as returned by the attachment upload endpoint. Required: a missing, null or non-UUID id is rejected with HTTP 400 and the message is not created.
          "type"?:  // Optional
          {
          }
          "filename": string
          "file": string (uri)
          "expiresAt": string // ISO datetime when this attachment will be auto-deleted.

For conversation-bound attachments: conversation.last_message_created_at + retention_days.
For attachments without a conversation: created_at + retention_days.
Returns None when cleanup is disabled (effective_from not set).
          "conversation"?: string (uuid) // Optional
        }
      ]
      "citations": [
        {
          "id": string (uuid)
          "filename": string // File name
          "file": string (uri) // File to upload
          "fileType": string
          "knowledgeBase"?:  // Optional
          {
            "id": string (uuid)
            "name": string
          }
          "size": integer // File size in bytes
          "status": 
          {
          }
          "parser": 
          {
            "id": string (uuid)
            "name": string
            "provider": 
            {
            }
            "isTimestampSttProvider": boolean
          }
          "labels"?: [ // Optional
            {
              "id": string (uuid)
              "name": string
            }
          ]
          "rawUserDefineMetadata"?: object // Optional
          "speakerLabels": [
            {
              "id": string (uuid)
              "originalLabel": string // Original speaker label from diarization (e.g., SPEAKER_00)
              "customName"?: string // User-defined speaker name (e.g., John) (Optional)
              "displayName": string
            }
          ]
          "vectorStorageSize": integer // Size of vectors for this file in Elasticsearch (bytes)
          "chunksCount": integer // Number of chunks/nodes generated from this file
          "waitingTime": number (double)
          "processingTime": number (double)
          "processingTimeDetails": object
          "ocrStatus": string // OCR result for scanned pages: done, skipped, partial, off; null before parsing or for legacy files
          "ocrSkippedReason": string // Reason OCR was not performed on scanned pages: no_azure_settings, azure_unavailable, insufficient_credits, transient_error, time_budget, disabled; null when completed
          "previewUrl": string // For presentations (pptx/ppt), returns the URL of a derived PDF for preview in the frontend PDF viewer; None for other file types.

Generate URLs using the same CustomizedFileFieldSerializer as the `file` field to ensure a consistent format (including each
storage provider's presign/host rules). Do not use get_absolute_url(): it produces incorrectly formatted URLs.
          "createdAt": string (timestamp)
        }
      ]
      "citationNodes": [
        {
          "chatbotTextNode": {
          {
            "id": string (uuid)
            "charactersCount": integer
            "hitsCount": integer
            "text": string
            "updatedAt": string (timestamp)
            "filename": string
            "chatbotFile": object // Return full ChatbotFile information, including the file URL and type, to support frontend image previews
            "knowledgeBaseFile": object // Same as get_chatbot_file, for backward compatibility
            "pageNumber": integer // Backward-compatible alias of ``page_start``.
            "pageStart": integer
            "pageEnd": integer
            "citationTitle": string // Source title shown on the inline citation hover card.
            "citationDescription": string // Source description shown on the inline citation hover card.
            "citationQuote": string // Quoted text snippet shown on the inline citation hover card.
            "hasImage": boolean // Determine whether images are present (check the file type or Markdown images in text)
            "imageUrl": string // Extract the image URL (prefer the file URL; otherwise extract it from Markdown)
            "displayText": string // Return the full text with Markdown image markup removed
            "displayTitle": string // Return the display title (fallback: citation_title -> filename)
            "highlightedText": string // Return text with matched terms wrapped in ``<mark>`` tags.

Priority:
1. ES/OpenSearch native highlight (set by retrieve_api via ``_es_highlighted_text``)
2. Python regex fallback (keyword-based, works for all backends)
            "labels": [
              {
                "id": string (uuid)
                "name": string
              }
            ]
            "rawUserDefineMetadata"?: object // Metadata keys and values defined by the user. (Optional)
            "metadataEnabledForSearch": object // Map each user-defined metadata key on this node to whether it was sent into the LLM.

A key reaches the LLM only when its ``MetadataKey.enabled_for_search`` is True *and*
the node carries a value for it. Since this maps over ``raw_user_define_metadata``
(the keys the cited-documents block displays, all of which have a value),
``enabled_for_search`` alone decides the flag. The source of truth is the node's
knowledge base ``MetadataKey`` current setting, matching the AI Search indicator.
            "startCharIdx": integer
            "endCharIdx": integer
          }
          }
          "score"?: number (double) // Optional
          "displayScore": integer
          "citationNumber"?: integer // Citation number matching the [1], [2] markers in the reply content (Optional)
          "highlightedText"?: string // ES/OpenSearch highlight result with <mark> tags (Optional)
        }
      ]
      "canvas"?: { // Optional
      {
        "id": string (uuid)
        "name": string
        "canvasType": 
        {
        }
        "title": string
        "content": string
        "createdAt": string (timestamp)
      }
      }
      "slideTemplate"?:  // Optional
      {
        "slug": string
        "displayName": string
        "thumbnailUrls"?: [ // Optional
          string
        ]
      }
      "queryMetadata"?: { // Query metadata controlling the scope of searchable knowledge; see each endpoint description for details (Optional)
      {
        "knowledgeBases"?: [ // List of searchable knowledge bases; an empty array or null denies access to all knowledge bases, while omission imposes no restriction (Optional)
          {
            "knowledgeBaseId": string (uuid) // Knowledge base ID
            "chatbotFileIds"?: [ // List of file IDs (excluded or exclusively selected, depending on hasUserSelectedAll) (Optional)
              string (uuid)
            ]
            "knowledgeBaseFileIds"?: [ // List of file IDs (the new name for chatbotFileIds; this field takes precedence when both are provided) (Optional)
              string (uuid)
            ]
            "faqIds"?: [ // List of FAQ IDs (excluded or exclusively selected, depending on hasUserSelectedAll) (Optional)
              string (uuid)
            ]
            "hasUserSelectedAll"?: boolean // true selects all knowledge base content except the listed items; false selects only the listed items (Optional)
          }
        ]
        "labelRelations"?: { // Label filter criteria; an empty conditions array applies no label filtering (it does not deny access). Must be provided together with knowledgeBases; when provided alone, no label filtering is applied to knowledge base retrieval (equivalent to no restriction) (Optional)
        {
          "operator": string (enum: AND, OR) // How label conditions are combined
          "conditions"?: [ // List of label conditions, supporting nested operator/conditions combinations (Optional)
            object
          ]
        }
        }
      }
      }
      "metadata"?: object // Message metadata, such as timezone and other environment information (Optional)
      "recordId": string // Return the ChatbotRecord ID associated with the Message

Note:
    For an unsaved Message (such as a virtual Message during streaming), return None directly,
    avoiding a database query through the OneToOneField reverse relation (the N+1 problem).
      "broadcast"?: boolean // Optional
      "skipCopilotTrigger"?: boolean // Optional
      "activeRevisionNumber": integer
      "revisions": [
        {
          "id": string (uuid)
          "revisionNumber": integer
          "content": string
          "contentPayload": object
          "isActive": boolean
          "createdAt": string (timestamp)
        }
      ]
      "suggestedForId"?: string (uuid) // Optional
      "suggestionStatus": string
      "suggestionTriggerSource": string
      "suggestionError": string
      "retryCount": integer // How many times the LLM call was auto-retried during this message generation. Currently only Bedrock first-chunk stall triggers retry (max 1). Detailed per-attempt records live in metadata["retry_attempts"].
      "skillIds"?: [ // Per-message selected skill IDs from the WebChat client (camelCase: skillIds). (Optional)
        string (uuid)
      ]
      "toolIds"?: [ // Per-message selected tool IDs from the WebChat client (camelCase: toolIds). (Optional)
        string (uuid)
      ]
      "templateId"?: string (uuid) // Document/sheet template ID for file-level compilation (camelCase: templateId). (Optional)
      "videoGeneration": object // Return the latest GeneratedVideo state for FE reload recovery;
``errorMessage`` is blank to match the socket emit (raw value stays on the DB row).
      "isGreeting": boolean // Whether this message is a greeting message.
    }
  ]
}
```

**Example Response Values**

```json
{
  "count": 123,
  "next": "http://api.example.org/accounts/?page=4",
  "previous": "http://api.example.org/accounts/?page=2",
  "results": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "conversation": "550e8400-e29b-41d4-a716-446655440000",
      "sender": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string",
        "avatar": "Response string",
        "email": "response@example.com",
        "phoneNumber": "Response string"
      },
      "type": "Response string",
      "content": "Response string",
      "contentPayload": null,
      "feedback": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "type": {},
        "suggestion": "Response string",
        "updatedAt": "Response string"
      },
      "createdAt": "Response string",
      "attachments": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "type": {},
          "filename": "Response string",
          "file": "https://example.com/file.jpg",
          "expiresAt": "Response string",
          "conversation": "550e8400-e29b-41d4-a716-446655440000"
        }
      ],
      "citations": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "filename": "Response string",
          "file": "https://example.com/file.jpg",
          "fileType": "Response string",
          "knowledgeBase": {
            "id": "550e8400-e29b-41d4-a716-446655440000",
            "name": "Response string"
          },
          "size": 456,
          "status": {},
          "parser": {
            "id": "550e8400-e29b-41d4-a716-446655440000",
            "name": "Response string",
            "provider": {},
            "isTimestampSttProvider": false
          },
          "labels": [
            {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "name": "Response string"
            }
          ],
          "rawUserDefineMetadata": null,
          "speakerLabels": [
            {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "originalLabel": "Response string",
              "customName": "Response string",
              "displayName": "Response string"
            }
          ],
          "vectorStorageSize": 456,
          "chunksCount": 456,
          "waitingTime": 456,
          "processingTime": 456,
          "processingTimeDetails": null,
          "ocrStatus": "Response string",
          "ocrSkippedReason": "Response string",
          "previewUrl": "Response string",
          "createdAt": "Response string"
        }
      ],
      "citationNodes": [
        {
          "chatbotTextNode": {
            "id": "550e8400-e29b-41d4-a716-446655440000",
            "charactersCount": 456,
            "hitsCount": 456,
            "text": "Response string",
            "updatedAt": "Response string",
            "filename": "Response string",
            "chatbotFile": {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "name": "Example response name",
              "description": "Example response description"
            },
            "knowledgeBaseFile": {
              "id": "550e8400-e29b-41d4-a716-446655440000",
              "name": "Example response name",
              "description": "Example response description"
            },
            "pageNumber": 456,
            "pageStart": 456,
            "pageEnd": 456,
            "citationTitle": "Response string",
            "citationDescription": "Response string",
            "citationQuote": "Response string",
            "hasImage": false,
            "imageUrl": "Response string",
            "displayText": "Response string",
            "displayTitle": "Response string",
            "highlightedText": "Response string",
            "labels": [
              {
                "id": "550e8400-e29b-41d4-a716-446655440000",
                "name": "Response string"
              }
            ],
            "rawUserDefineMetadata": null,
            "metadataEnabledForSearch": {
              "key1": "value1",
              "key2": "value2",
              "createdAt": "2025-01-01T00:00:00.000Z"
            },
            "startCharIdx": 456,
            "endCharIdx": 456
          },
          "score": 456,
          "displayScore": 456,
          "citationNumber": 456,
          "highlightedText": "Response string"
        }
      ],
      "canvas": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string",
        "canvasType": {},
        "title": "Response string",
        "content": "Response string",
        "createdAt": "Response string"
      },
      "slideTemplate": {
        "slug": "Response string",
        "displayName": "Response string",
        "thumbnailUrls": [
          "Response string"
        ]
      },
      "queryMetadata": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      },
      "metadata": null,
      "recordId": "Response string",
      "broadcast": false,
      "skipCopilotTrigger": false,
      "activeRevisionNumber": 456,
      "revisions": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "revisionNumber": 456,
          "content": "Response string",
          "contentPayload": null,
          "isActive": false,
          "createdAt": "Response string"
        }
      ],
      "suggestedForId": "550e8400-e29b-41d4-a716-446655440000",
      "suggestionStatus": "Response string",
      "suggestionTriggerSource": "Response string",
      "suggestionError": "Response string",
      "retryCount": 456,
      "skillIds": [
        "550e8400-e29b-41d4-a716-446655440000"
      ],
      "toolIds": [
        "550e8400-e29b-41d4-a716-446655440000"
      ],
      "templateId": "550e8400-e29b-41d4-a716-446655440000",
      "videoGeneration": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      },
      "isGreeting": false
    }
  ]
}
```

***

### Retrieve a Specific Message <a href="#messages-retrieve" id="messages-retrieve"></a>

GET `/api/v1/messages/{id}/`

#### Parameters

| Parameter Name | Required | Type   | Description                             |
| -------------- | -------- | ------ | --------------------------------------- |
| `id`           | ✅        | string | A UUID string identifying this Message. |
| `conversation` | ✅        | string | Conversation ID                         |

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X GET "https://api.maiagent.ai/api/v1/messages/550e8400-e29b-41d4-a716-446655440000/?conversation=example" \
  -H "Authorization: Api-Key YOUR_API_KEY"

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY'
  }
};

axios.get("https://api.maiagent.ai/api/v1/messages/550e8400-e29b-41d4-a716-446655440000/?conversation=example", config)
  .then(response => {
    console.log('Response retrieved successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/messages/550e8400-e29b-41d4-a716-446655440000/?conversation=example"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY"
}


response = requests.get(url, headers=headers)
try:
    print("Response retrieved successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->get("https://api.maiagent.ai/api/v1/messages/550e8400-e29b-41d4-a716-446655440000/?conversation=example", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response retrieved successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 200**

**Example Response Schema**

```typescript
{
  "id": string (uuid)
  "conversation": string (uuid)
  "sender": 
  {
    "id": string (uuid)
    "name": string
    "avatar": string
    "email"?: string (email) // Optional
    "phoneNumber"?: string // Optional
  }
  "type"?: string // Optional
  "content"?: string // Optional
  "contentPayload"?: object // Optional
  "feedback": 
  {
    "id": string (uuid)
    "type": 
    {
    }
    "suggestion"?: string // Optional
    "updatedAt": string (timestamp)
  }
  "createdAt": string (timestamp)
  "attachments"?: [ // Optional
    {
      "id": string (uuid) // ID of an already-uploaded attachment, as returned by the attachment upload endpoint. Required: a missing, null or non-UUID id is rejected with HTTP 400 and the message is not created.
      "type"?:  // Optional
      {
      }
      "filename": string
      "file": string (uri)
      "expiresAt": string // ISO datetime when this attachment will be auto-deleted.

For conversation-bound attachments: conversation.last_message_created_at + retention_days.
For attachments without a conversation: created_at + retention_days.
Returns None when cleanup is disabled (effective_from not set).
      "conversation"?: string (uuid) // Optional
    }
  ]
  "citations": [
    {
      "id": string (uuid)
      "filename": string // File name
      "file": string (uri) // File to upload
      "fileType": string
      "knowledgeBase"?:  // Optional
      {
        "id": string (uuid)
        "name": string
      }
      "size": integer // File size in bytes
      "status": 
      {
      }
      "parser": 
      {
        "id": string (uuid)
        "name": string
        "provider": 
        {
        }
        "isTimestampSttProvider": boolean
      }
      "labels"?: [ // Optional
        {
          "id": string (uuid)
          "name": string
        }
      ]
      "rawUserDefineMetadata"?: object // Optional
      "speakerLabels": [
        {
          "id": string (uuid)
          "originalLabel": string // Original speaker label from diarization (e.g., SPEAKER_00)
          "customName"?: string // User-defined speaker name (e.g., John) (Optional)
          "displayName": string
        }
      ]
      "vectorStorageSize": integer // Size of vectors for this file in Elasticsearch (bytes)
      "chunksCount": integer // Number of chunks/nodes generated from this file
      "waitingTime": number (double)
      "processingTime": number (double)
      "processingTimeDetails": object
      "ocrStatus": string // OCR result for scanned pages: done, skipped, partial, off; null before parsing or for legacy files
      "ocrSkippedReason": string // Reason OCR was not performed on scanned pages: no_azure_settings, azure_unavailable, insufficient_credits, transient_error, time_budget, disabled; null when completed
      "previewUrl": string // For presentations (pptx/ppt), returns the URL of a derived PDF for preview in the frontend PDF viewer; None for other file types.

Generate URLs using the same CustomizedFileFieldSerializer as the `file` field to ensure a consistent format (including each
storage provider's presign/host rules). Do not use get_absolute_url(): it produces incorrectly formatted URLs.
      "createdAt": string (timestamp)
    }
  ]
  "citationNodes": [
    {
      "chatbotTextNode": {
      {
        "id": string (uuid)
        "charactersCount": integer
        "hitsCount": integer
        "text": string
        "updatedAt": string (timestamp)
        "filename": string
        "chatbotFile": object // Return full ChatbotFile information, including the file URL and type, to support frontend image previews
        "knowledgeBaseFile": object // Same as get_chatbot_file, for backward compatibility
        "pageNumber": integer // Backward-compatible alias of ``page_start``.
        "pageStart": integer
        "pageEnd": integer
        "citationTitle": string // Source title shown on the inline citation hover card.
        "citationDescription": string // Source description shown on the inline citation hover card.
        "citationQuote": string // Quoted text snippet shown on the inline citation hover card.
        "hasImage": boolean // Determine whether images are present (check the file type or Markdown images in text)
        "imageUrl": string // Extract the image URL (prefer the file URL; otherwise extract it from Markdown)
        "displayText": string // Return the full text with Markdown image markup removed
        "displayTitle": string // Return the display title (fallback: citation_title -> filename)
        "highlightedText": string // Return text with matched terms wrapped in ``<mark>`` tags.

Priority:
1. ES/OpenSearch native highlight (set by retrieve_api via ``_es_highlighted_text``)
2. Python regex fallback (keyword-based, works for all backends)
        "labels": [
          {
            "id": string (uuid)
            "name": string
          }
        ]
        "rawUserDefineMetadata"?: object // Metadata keys and values defined by the user. (Optional)
        "metadataEnabledForSearch": object // Map each user-defined metadata key on this node to whether it was sent into the LLM.

A key reaches the LLM only when its ``MetadataKey.enabled_for_search`` is True *and*
the node carries a value for it. Since this maps over ``raw_user_define_metadata``
(the keys the cited-documents block displays, all of which have a value),
``enabled_for_search`` alone decides the flag. The source of truth is the node's
knowledge base ``MetadataKey`` current setting, matching the AI Search indicator.
        "startCharIdx": integer
        "endCharIdx": integer
      }
      }
      "score"?: number (double) // Optional
      "displayScore": integer
      "citationNumber"?: integer // Citation number matching the [1], [2] markers in the reply content (Optional)
      "highlightedText"?: string // ES/OpenSearch highlight result with <mark> tags (Optional)
    }
  ]
  "canvas"?: { // Optional
  {
    "id": string (uuid)
    "name": string
    "canvasType": 
    {
    }
    "title": string
    "content": string
    "createdAt": string (timestamp)
  }
  }
  "slideTemplate"?:  // Optional
  {
    "slug": string
    "displayName": string
    "thumbnailUrls"?: [ // Optional
      string
    ]
  }
  "queryMetadata"?: { // Query metadata that controls which knowledge can be queried; see each endpoint for details (Optional)
  {
    "knowledgeBases"?: [ // List of queryable knowledge bases; an empty array or null means none can be queried (no permission); omitting it imposes no restriction (Optional)
      {
        "knowledgeBaseId": string (uuid) // Knowledge base ID
        "chatbotFileIds"?: [ // List of file IDs (excluded or exclusively selected, depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "knowledgeBaseFileIds"?: [ // List of file IDs (the new name for chatbotFileIds; this field takes precedence when both are provided) (Optional)
          string (uuid)
        ]
        "faqIds"?: [ // List of FAQ IDs (excluded or exclusively selected, depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "hasUserSelectedAll"?: boolean // true selects all knowledge base content except the listed items; false selects only the listed items (Optional)
      }
    ]
    "labelRelations"?: { // Label filter criteria; an empty conditions array means no label filtering (not a lack of permission). Must be provided together with knowledgeBases—if provided alone, knowledge base retrieval does not apply label filtering (equivalent to no restriction) (Optional)
    {
      "operator": string (enum: AND, OR) // How label conditions are combined
      "conditions"?: [ // List of label conditions; supports nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "metadata"?: object // Message metadata, such as timezone and other environment information (Optional)
  "recordId": string // Return the ChatbotRecord ID corresponding to the Message

Note:
    For unsaved Messages (such as virtual Messages during streaming), return None directly,
    avoiding a DB query on the OneToOneField reverse relation (the N+1 problem).
  "broadcast"?: boolean // Optional
  "skipCopilotTrigger"?: boolean // Optional
  "activeRevisionNumber": integer
  "revisions": [
    {
      "id": string (uuid)
      "revisionNumber": integer
      "content": string
      "contentPayload": object
      "isActive": boolean
      "createdAt": string (timestamp)
    }
  ]
  "suggestedForId"?: string (uuid) // Optional
  "suggestionStatus": string
  "suggestionTriggerSource": string
  "suggestionError": string
  "retryCount": integer // How many times the LLM call was auto-retried during this message generation. Currently only Bedrock first-chunk stall triggers retry (max 1). Detailed per-attempt records live in metadata["retry_attempts"].
  "skillIds"?: [ // Per-message selected skill IDs from the WebChat client (camelCase: skillIds). (Optional)
    string (uuid)
  ]
  "toolIds"?: [ // Per-message selected tool IDs from the WebChat client (camelCase: toolIds). (Optional)
    string (uuid)
  ]
  "templateId"?: string (uuid) // Document/sheet template ID for file-level compilation (camelCase: templateId). (Optional)
  "videoGeneration": object // Return the latest GeneratedVideo state for FE reload recovery;
``errorMessage`` is blank to match the socket emit (raw value stays on the DB row).
  "isGreeting": boolean // Whether this message is a greeting message.
}
```

**Example Response Values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "conversation": "550e8400-e29b-41d4-a716-446655440000",
  "sender": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "avatar": "Response string",
    "email": "response@example.com",
    "phoneNumber": "Response string"
  },
  "type": "Response string",
  "content": "Response string",
  "contentPayload": null,
  "feedback": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": {},
    "suggestion": "Response string",
    "updatedAt": "Response string"
  },
  "createdAt": "Response string",
  "attachments": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "type": {},
      "filename": "Response string",
      "file": "https://example.com/file.jpg",
      "expiresAt": "Response string",
      "conversation": "550e8400-e29b-41d4-a716-446655440000"
    }
  ],
  "citations": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "filename": "Response string",
      "file": "https://example.com/file.jpg",
      "fileType": "Response string",
      "knowledgeBase": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string"
      },
      "size": 456,
      "status": {},
      "parser": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string",
        "provider": {},
        "isTimestampSttProvider": false
      },
      "labels": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "name": "Response string"
        }
      ],
      "rawUserDefineMetadata": null,
      "speakerLabels": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "originalLabel": "Response string",
          "customName": "Response string",
          "displayName": "Response string"
        }
      ],
      "vectorStorageSize": 456,
      "chunksCount": 456,
      "waitingTime": 456,
      "processingTime": 456,
      "processingTimeDetails": null,
      "ocrStatus": "Response string",
      "ocrSkippedReason": "Response string",
      "previewUrl": "Response string",
      "createdAt": "Response string"
    }
  ],
  "citationNodes": [
    {
      "chatbotTextNode": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "charactersCount": 456,
        "hitsCount": 456,
        "text": "Response string",
        "updatedAt": "Response string",
        "filename": "Response string",
        "chatbotFile": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "name": "Example response name",
          "description": "Example response description"
        },
        "knowledgeBaseFile": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "name": "Example response name",
          "description": "Example response description"
        },
        "pageNumber": 456,
        "pageStart": 456,
        "pageEnd": 456,
        "citationTitle": "Response string",
        "citationDescription": "Response string",
        "citationQuote": "Response string",
        "hasImage": false,
        "imageUrl": "Response string",
        "displayText": "Response string",
        "displayTitle": "Response string",
        "highlightedText": "Response string",
        "labels": [
          {
            "id": "550e8400-e29b-41d4-a716-446655440000",
            "name": "Response string"
          }
        ],
        "rawUserDefineMetadata": null,
        "metadataEnabledForSearch": {
          "key1": "value1",
          "key2": "value2",
          "createdAt": "2025-01-01T00:00:00.000Z"
        },
        "startCharIdx": 456,
        "endCharIdx": 456
      },
      "score": 456,
      "displayScore": 456,
      "citationNumber": 456,
      "highlightedText": "Response string"
    }
  ],
  "canvas": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "canvasType": {},
    "title": "Response string",
    "content": "Response string",
    "createdAt": "Response string"
  },
  "slideTemplate": {
    "slug": "Response string",
    "displayName": "Response string",
    "thumbnailUrls": [
      "Response string"
    ]
  },
  "queryMetadata": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "metadata": null,
  "recordId": "Response string",
  "broadcast": false,
  "skipCopilotTrigger": false,
  "activeRevisionNumber": 456,
  "revisions": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "revisionNumber": 456,
      "content": "Response string",
      "contentPayload": null,
      "isActive": false,
      "createdAt": "Response string"
    }
  ],
  "suggestedForId": "550e8400-e29b-41d4-a716-446655440000",
  "suggestionStatus": "Response string",
  "suggestionTriggerSource": "Response string",
  "suggestionError": "Response string",
  "retryCount": 456,
  "skillIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "toolIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "templateId": "550e8400-e29b-41d4-a716-446655440000",
  "videoGeneration": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "isGreeting": false
}
```

***

### List Conversations <a href="#conversations-list" id="conversations-list"></a>

GET `/api/v1/conversations/`

#### Parameters

| Parameter Name           | Required | Type    | Description                                                   |
| ------------------------ | -------- | ------- | ------------------------------------------------------------- |
| `assignee`               | ❌        | string  |                                                               |
| `chatbot`                | ❌        | string  |                                                               |
| `contact`                | ❌        | string  | Contact ID                                                    |
| `contactTags`            | ❌        | array   | Multiple values may be separated by commas.                   |
| `cursor`                 | ❌        | string  | The pagination cursor value.                                  |
| `endDate`                | ❌        | string  |                                                               |
| `externalConversationId` | ❌        | string  |                                                               |
| `externalSource`         | ❌        | string  |                                                               |
| `inbox`                  | ❌        | string  | Inbox ID                                                      |
| `isPinned`               | ❌        | string  |                                                               |
| `keyword`                | ❌        | string  | Keyword search                                                |
| `kind`                   | ❌        | string  | \`chat\`: Chat ; \`secretary\`: Secretary;                    |
| `mine`                   | ❌        | boolean |                                                               |
| `mode`                   | ❌        | string  | \`chat\`: Chat ; \`cowork\`: Cowork;                          |
| `pageSize`               | ❌        | integer | Number of results to return per page.                         |
| `startDate`              | ❌        | string  |                                                               |
| `status`                 | ❌        | array   | \`open\`: Open ; \`resolved\`: Resolved ; \`queued\`: Queued; |
| `tags`                   | ❌        | array   | Multiple values may be separated by commas.                   |
| `unassigned`             | ❌        | boolean |                                                               |
| `updatedAfter`           | ❌        | string  |                                                               |

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X GET "https://api.maiagent.ai/api/v1/conversations/?assignee=550e8400-e29b-41d4-a716-446655440000&chatbot=550e8400-e29b-41d4-a716-446655440000&contact=550e8400-e29b-41d4-a716-446655440000&contactTags=example&cursor=example&endDate=example&externalConversationId=example&externalSource=example&inbox=550e8400-e29b-41d4-a716-446655440000&isPinned=example&keyword=example&kind=chat&mine=true&mode=chat&pageSize=1&startDate=example&status=example&tags=example&unassigned=true&updatedAfter=2025-01-01T00:00:00.000Z" \
  -H "Authorization: Api-Key YOUR_API_KEY"

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY'
  }
};

axios.get("https://api.maiagent.ai/api/v1/conversations/?assignee=550e8400-e29b-41d4-a716-446655440000&chatbot=550e8400-e29b-41d4-a716-446655440000&contact=550e8400-e29b-41d4-a716-446655440000&contactTags=example&cursor=example&endDate=example&externalConversationId=example&externalSource=example&inbox=550e8400-e29b-41d4-a716-446655440000&isPinned=example&keyword=example&kind=chat&mine=true&mode=chat&pageSize=1&startDate=example&status=example&tags=example&unassigned=true&updatedAfter=2025-01-01T00:00:00.000Z", config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/conversations/?assignee=550e8400-e29b-41d4-a716-446655440000&chatbot=550e8400-e29b-41d4-a716-446655440000&contact=550e8400-e29b-41d4-a716-446655440000&contactTags=example&cursor=example&endDate=example&externalConversationId=example&externalSource=example&inbox=550e8400-e29b-41d4-a716-446655440000&isPinned=example&keyword=example&kind=chat&mine=true&mode=chat&pageSize=1&startDate=example&status=example&tags=example&unassigned=true&updatedAfter=2025-01-01T00:00:00.000Z"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY"
}


response = requests.get(url, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->get("https://api.maiagent.ai/api/v1/conversations/?assignee=550e8400-e29b-41d4-a716-446655440000&chatbot=550e8400-e29b-41d4-a716-446655440000&contact=550e8400-e29b-41d4-a716-446655440000&contactTags=example&cursor=example&endDate=example&externalConversationId=example&externalSource=example&inbox=550e8400-e29b-41d4-a716-446655440000&isPinned=example&keyword=example&kind=chat&mine=true&mode=chat&pageSize=1&startDate=example&status=example&tags=example&unassigned=true&updatedAfter=2025-01-01T00:00:00.000Z", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response

**Status Code: 200**

**Example Response Schema**

```typescript
{
  "count": integer
  "next"?: string (uri) // Optional
  "previous"?: string (uri) // Optional
  "results": [
    {
      "id": string (uuid)
      "contact": { // Lightweight Contact Serializer for the Conversation List view

Includes only the fields required by the List view, omitting inboxes, mcp_credentials, and api_credentials
to avoid N+1 queries. email / phone_number / metadata are scalar fields on Contact itself,
needed for display in the frontend conversation sidebar, with no N+1 cost.
      {
        "id": string (uuid)
        "name": string
        "avatar"?: string // Stores our own media as a relative storage key, or an external CDN URL as-is. (Optional)
        "email"?: // May have different types (Optional)
        string (email) // Optional
        "phoneNumber"?: string // Optional
        "metadata"?: object // Custom customer information shown in the customer panel of the conversation page (Optional)
        "sourceId"?: string // Optional
        "tags": [
          {
            "id": string (uuid)
            "name": string
          }
        ]
      }
      }
      "inbox": { // Inbox summary embedded in a conversation payload.

``unread_conversations_count`` stays here because it is part of the
documented REST conversation API. It is an org-wide denormalized
counter, so WS emit paths must run the payload through
:func:`strip_org_wide_unread_count` before broadcasting it to rooms
that include members without ``GroupInbox.can_view_conversations``.
      {
        "id": string (uuid)
        "name": string
        "channelType": string (enum: line, telegram, teams, web, messenger, instagram, email, whatsapp, slack, team_plus, line_works, viber) // * `line` - LINE
* `telegram` - Telegram
* `teams` - Teams
* `web` - Web
* `messenger` - Messenger
* `instagram` - Instagram
* `email` - Email
* `whatsapp` - WhatsApp
* `slack` - Slack
* `team_plus` - Team+
* `line_works` - LINE WORKS
* `viber` - Viber
        "unreadConversationsCount"?: integer // Optional
        "signAuth"?:  // Optional
        {
          "id": string (uuid)
          "signSource": string (uuid)
          "signParams": {
          {
            "keycloak": 
            {
              "clientId": string
              "url": string
              "realm": string
            }
            "line": 
            {
              "liffId": string
            }
            "ad": 
            {
              "saml": string
            }
            "google"?:  // Optional
            {
              "enabled": boolean
            }
          }
          }
          "sourceIdAccessEnabled"?: boolean // When enabled, embedded visitors presenting a Source ID (verified per the verify mode below) may chat without going through the login flow. Disabled keeps every existing behavior unchanged. (Optional)
          "sourceIdVerifyMode"?:  // "Require signature verification": the embedding site must send an HMAC signature computed on its backend. "Source ID only": anyone knowing a Source ID can act as that contact; suitable only for testing or closed networks.

* `signature` - Require signature verification
* `raw` - Source ID only (Optional)
          {
          }
          "sourceIdSignatureTtlMinutes"?:  // How long a signature can be used to obtain the contact identity. Not a session lifetime: once exchanged, the session follows the existing WebChat mechanism.

* `1` - 1
* `5` - 5
* `15` - 15 (Optional)
          {
          }
          "keycloakTokenPassthrough"?: boolean // When enabled, the Keycloak access token a visitor logged in with is forwarded as the Authorization header when the AI calls MCP and API tools on their behalf, so tools do not require a separate per-tool login. Only for the Keycloak sign source. Never overrides an Authorization header a tool already resolves from its own credentials. The visitor identity is the organization member bound to the conversation at login, never the contact Source ID. (Optional)
          "keycloakTokenPassthroughAllowedHosts"?: [ // Hostnames (or "*.example.com" wildcards) of the tools the visitor Keycloak token may be sent to (Optional)
            string
          ]
          "organizationLogin": object // How visitors sign in when the sign source is "organization" (mirrors the back-office login); null for other sign sources
        }
        "enableAnalysis"?: boolean // Optional
      }
      }
      "chatbot": object // Chatbot (Agent) summary from the conversation's inbox, already eager-loaded.

``is_maigpt`` lets the client tell the default MaiGPT agent apart (icon, feature
gate) without comparing against the organization's ``maigpt_inbox`` id;
``file_workspace_mode`` is the effective P6 mode (MaiGPT, blueprint and the
organization's file-library switch already applied).
      "title": string
      "lastMessage"?: { // Optional
      {
        "id": string (uuid)
        "type"?: string // Optional
        "content"?: string // Optional
        "createdAt": string (timestamp)
      }
      }
      "callRecord": 
      {
        "id": string (uuid)
        "status"?: string (enum: initiated, ringing, in-progress, completed, failed, busy, no-answer, canceled) // * `initiated` - Initiated
* `ringing` - Ringing
* `in-progress` - In Progress
* `completed` - Completed
* `failed` - Failed
* `busy` - Busy
* `no-answer` - No Answer
* `canceled` - Canceled (Optional)
        "direction": string (enum: inbound, outbound) // * `inbound` - Inbound
* `outbound` - Outbound
        "isActive": boolean
        "canHangup": boolean
      }
      "lastMessageCreatedAt": string (timestamp)
      "unreadMessagesCount": integer
      "autoReplyEnabled": boolean
      "isAutoReplyNow": boolean
      "willAiReplyNow": boolean // Whether a text message arriving through the inbox gets an AI reply right now.

The messaging channels are gated by ``is_auto_reply_now`` (auto reply on and inside the
inbox auto-reply schedule) and by the inbox having a chatbot. The chatbot's API web chat
is answered by the completions endpoint, which ignores the schedule, so there the
conversation switch alone decides. The channel is only looked up for a web inbox outside
its schedule.

Surfaces that ignore the schedule on any inbox answer from their own rule instead: the
completions endpoint continuing an existing conversation (``completions_progress_status``)
and voice turns, which the voice agent answers.
      "lastReadAt": string (timestamp)
      "createdAt": string (timestamp)
      "isGroupChat": boolean
      "enableGroupMention"?: boolean // Optional
      "queryMetadata"?: { // Query metadata that controls the scope of searchable knowledge; see each endpoint for details (Optional)
      {
        "knowledgeBases"?: [ // List of searchable knowledge bases; an empty array or null denies all access (no permission); omitting it imposes no restriction (Optional)
          {
            "knowledgeBaseId": string (uuid) // Knowledge base ID
            "chatbotFileIds"?: [ // List of file IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
              string (uuid)
            ]
            "knowledgeBaseFileIds"?: [ // List of file IDs (the new name for chatbotFileIds; this field takes precedence when both are provided) (Optional)
              string (uuid)
            ]
            "faqIds"?: [ // List of FAQ IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
              string (uuid)
            ]
            "hasUserSelectedAll"?: boolean // true selects all knowledge base content except the listed items; false selects only the listed items (Optional)
          }
        ]
        "labelRelations"?: { // Label filter criteria; an empty conditions array means no label filtering (not a lack of permission). Must be provided with knowledgeBases; when provided alone, knowledge base retrieval does not apply label filtering (no restriction) (Optional)
        {
          "operator": string (enum: AND, OR) // How label conditions are combined
          "conditions"?: [ // List of label conditions; supports nested operator/conditions combinations (Optional)
            object
          ]
        }
        }
      }
      }
      "toolMask"?: object // Enabled/disabled state per tool: {tool_id: bool} (Optional)
      "connectorMask"?: object // Enabled/disabled state per connector: {connector_id: bool} (Optional)
      "thinkingConfig"?: object // None = not set (inherit the chatbot setting); otherwise {"reasoning_effort": <level>} (Optional)
      "thinkingConfigSchema": { // Thinking configuration form for this model; null means the model has no thinking configuration
      {
        "type"?: string // Form type, always reasoning_effort (Optional)
        "fields"?: object // reasoning_effort field: options lists the thinking levels this model accepts (from lowest to highest); default is always null (Optional)
        "canDisable"?: boolean // Whether thinking can be disabled for this model; when true, options includes off (Optional)
        "unsetBehavior"?: string (enum: off, model_default) // Behavior when neither the conversation nor the assistant sets a thinking level: off disables thinking (fastest); model_default lets the model decide, or enables thinking through the platform default level (Optional)
        "unsetLevel"?: string // Level the platform actually sends when no level is set; null means no thinking parameter is sent, leaving the model to use its own default (Optional)
      }
      }
      "effectiveThinkingConfig": object // The level the next reply sends on this conversation's model, after gating and clamping.

The stored choice resolves conversation > chatbot > the LLM row's default level; None means
the model sends no thinking parameter, so the provider default applies.
      "llm": string (uuid) // The model the next reply runs on: the conversation's pin, else its assistant's current model.

TC-0205: returning the stored ``conversation.llm`` would show a model an unpinned conversation no
longer uses once its assistant switches model, while ``thinking_config_schema`` already follows the
effective model. Read-only: displaying it never marks the conversation as explicitly selected;
only the update-llm endpoint does.
      "deepResearchStatus":  // Status of deep research for this conversation

* `not_used` - Not Used
* `started` - Started
* `running` - Running
* `completed` - Completed
* `failed` - Failed
      {
      }
      "deepResearchOutputFormat": // May have different types
      string (enum: canvas, chat, file) // Chosen delivery form for the finished report. Set only when the user clicks a format in the pre-research choice card; reset to NULL each time Deep Research is (re-)armed. NULL means no explicit choice (or a plan-skipping request) and falls back to Canvas at render time, but stays distinct from an explicit canvas pick so the frontend can lock the card only after a real selection. To roll back to fixed-Canvas behavior, force this to NULL/canvas — no redeploy needed.

* `canvas` - Canvas
* `chat` - Chat reply
* `file` - Downloadable file
      "ipAddress": string
      "ipCountryCode": string
      "ipRecordedAt": string (timestamp)
      "assignee": 
      {
        "id": string (uuid)
        "name": string
        "userId": string (uuid)
      }
      "status"?: string (enum: open, resolved, queued) // * `open` - Open
* `resolved` - Resolved
* `queued` - Queued (Optional)
      "tags": [
        {
          "id": string (uuid)
          "name": string
          "memberIds"?: [ // Optional
            string (uuid)
          ]
        }
      ]
      "progressStatus": string
      "firstResponseAt"?: string (timestamp) // Optional
      "queuedAt"?: string (timestamp) // Optional
      "assignedAt"?: string (timestamp) // Optional
      "transferReason"?: string // Optional
      "satisfactionScore": integer // Conversation satisfaction rating on a 5-point scale (1=very dissatisfied, 5=very satisfied)
      "satisfactionComment": string
      "satisfactionSubmittedAt": string (timestamp)
      "latestAnalysisSummary": string // The conversation's current analysis summary (its active summary revision).

Reads the `latest_analysis_summary` annotation added by `setup_eager_loading`
to avoid an N+1 query. All read paths (list / retrieve / create) eager-load,
so the annotation is present; it falls back to '' only for instances built
outside `setup_eager_loading`.
      "clientPlatform":  // Client platform that created this conversation (web, desktop)

* `web` - Web
* `desktop` - Desktop
      {
      }
      "workingDirectory": string // Absolute path of the local folder bound to a desktop conversation. Only set for desktop conversations; null for web conversations.
      "slideProjectId": string
      "matchedField": string // Cached per instance: ``get_matched_message_id`` / ``get_matched_snippet``
both read this, so resolving it once avoids recomputing the keyword and
title-match logic three times per serialized conversation.
      "matchedSnippet": string // Keyword-centred excerpt of the matched message, with ellipses.

Reads the ``matched_message_content`` annotation added by
``ConversationViewSet._annotate_keyword_match``; no extra query.
      "matchedMessageId": string
      "pinnedAt": string (timestamp) // When the conversation was pinned. Null means unpinned.
      "folder": string (uuid) // The user-defined sidebar folder this conversation is grouped into. Null means unclassified (shown under the time groups). Deleting the folder resets this to null without deleting the conversation.
      "hasActiveBrowserSession": boolean // Whether a browser_use session snapshot exists to resume.

Lets the frontend know it should proactively re-request the browser
panel (emit ``browser:start``) after a page reload, instead of only
ever showing the panel when a live ``browser:frame`` push happens to
arrive first.
      "callerOrganizationId": string // Bound caller organization (stamped at conversation start); null when unbound
      "handoff": object // Handoff information: enabled is the platform toggle; the continuing conversation includes source_conversation_id/source_title; the original conversation includes continued_to_conversation_id/continued_to_title and latest_summary_message_id
      "displayContextUsage": boolean // Whether to display the context usage percentage bar (a conversation platform toggle; always true for MaiGPT; independent of the handoff toggle)
      "autoSplitReason": string // Reason automatic conversation splitting created this conversation: resolved (after resolution) / idle (inactivity); null if not created by splitting
      "continuedFrom": object // Conversation continued from when splitting: {id, last_message_at, deleted}; if the old conversation was deleted, id=null and deleted=true
      "continuedTo": object // Most recent conversation split from this conversation: {id, created_at}; null if none
      "continuationSummary": object // Continuation summary: {state: disabled|pending|ready|not_applied, items: [{conversation_id, content, generated_at, last_message_at, old_status}]} (items ordered newest to oldest); null if not created by splitting
      "conversationKind": string // chat = a chat with an agent; secretary = the member's personal secretary Work chat
    }
  ]
}
```

**Example Response Values**

```json
{
  "count": 123,
  "next": "http://api.example.org/accounts/?page=4",
  "previous": "http://api.example.org/accounts/?page=2",
  "results": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "contact": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string",
        "avatar": "Response string",
        "email": "response@example.com",
        "phoneNumber": "Response string",
        "metadata": null,
        "sourceId": "Response string",
        "tags": [
          {
            "id": "550e8400-e29b-41d4-a716-446655440000",
            "name": "Response string"
          }
        ]
      },
      "inbox": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string",
        "channelType": "line",
        "unreadConversationsCount": 456,
        "signAuth": {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "signSource": "550e8400-e29b-41d4-a716-446655440000",
          "signParams": {
            "keycloak": {
              "clientId": "Response string",
              "url": "Response string",
              "realm": "Response string"
            },
            "line": {
              "liffId": "Response string"
            },
            "ad": {
              "saml": "Response string"
            },
            "google": {
              "enabled": false
            }
          },
          "sourceIdAccessEnabled": false,
          "sourceIdVerifyMode": {},
          "sourceIdSignatureTtlMinutes": {},
          "keycloakTokenPassthrough": false,
          "keycloakTokenPassthroughAllowedHosts": [
            "Response string"
          ],
          "organizationLogin": {
            "id": "550e8400-e29b-41d4-a716-446655440000",
            "name": "Example response name",
            "description": "Example response description"
          }
        },
        "enableAnalysis": false
      },
      "chatbot": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      },
      "title": "Response string",
      "lastMessage": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "type": "Response string",
        "content": "Response string",
        "createdAt": "Response string"
      },
      "callRecord": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "status": "initiated",
        "direction": "inbound",
        "isActive": false,
        "canHangup": false
      },
      "lastMessageCreatedAt": "Response string",
      "unreadMessagesCount": 456,
      "autoReplyEnabled": false,
      "isAutoReplyNow": false,
      "willAiReplyNow": false,
      "lastReadAt": "Response string",
      "createdAt": "Response string",
      "isGroupChat": false,
      "enableGroupMention": false,
      "queryMetadata": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      },
      "toolMask": null,
      "connectorMask": null,
      "thinkingConfig": null,
      "thinkingConfigSchema": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      },
      "effectiveThinkingConfig": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      },
      "llm": "550e8400-e29b-41d4-a716-446655440000",
      "deepResearchStatus": {},
      "deepResearchOutputFormat": "canvas",
      "ipAddress": "Response string",
      "ipCountryCode": "Response string",
      "ipRecordedAt": "Response string",
      "assignee": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string",
        "userId": "550e8400-e29b-41d4-a716-446655440000"
      },
      "status": "open",
      "tags": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "name": "Response string",
          "memberIds": [
            "550e8400-e29b-41d4-a716-446655440000"
          ]
        }
      ],
      "progressStatus": "Response string",
      "firstResponseAt": "Response string",
      "queuedAt": "Response string",
      "assignedAt": "Response string",
      "transferReason": "Response string",
      "satisfactionScore": 456,
      "satisfactionComment": "Response string",
      "satisfactionSubmittedAt": "Response string",
      "latestAnalysisSummary": "Response string",
      "clientPlatform": {},
      "workingDirectory": "Response string",
      "slideProjectId": "Response string",
      "matchedField": "Response string",
      "matchedSnippet": "Response string",
      "matchedMessageId": "Response string",
      "pinnedAt": "Response string",
      "folder": "550e8400-e29b-41d4-a716-446655440000",
      "hasActiveBrowserSession": false,
      "callerOrganizationId": "Response string",
      "handoff": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      },
      "displayContextUsage": false,
      "autoSplitReason": "Response string",
      "continuedFrom": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      },
      "continuedTo": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      },
      "continuationSummary": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      },
      "conversationKind": "Response string"
    }
  ]
}
```

**Status code: 4XX - Error response; X-Error-Code carries its code (see "Error codes").**

***

### Retrieve a Specific Conversation <a href="#conversations-retrieve" id="conversations-retrieve"></a>

GET `/api/v1/conversations/{id}/`

#### Parameters

| Parameter Name | Required | Type   | Description                                  |
| -------------- | -------- | ------ | -------------------------------------------- |
| `id`           | ✅        | string | A UUID string identifying this Conversation. |

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X GET "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/" \
  -H "Authorization: Api-Key YOUR_API_KEY"

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY'
  }
};

axios.get("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/", config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY"
}


response = requests.get(url, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->get("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Content

**Status code: 200**

**Example Response Schema**

```typescript
{
  "id": string (uuid)
  "contact": { // Lightweight Contact Serializer for the Conversation List view

Includes only fields required by the List view, excluding inboxes, mcp_credentials, and api_credentials
to avoid N+1 queries. email / phone_number / metadata are scalar fields on Contact itself,
needed for display in the frontend conversation sidebar, with no N+1 overhead.
  {
    "id": string (uuid)
    "name": string
    "avatar"?: string // Stores our own media as a relative storage key, or an external CDN URL as-is. (Optional)
    "email"?: // May have different types (Optional)
    string (email) // Optional
    "phoneNumber"?: string // Optional
    "metadata"?: object // Custom customer information shown in the customer panel of the conversation page (Optional)
    "sourceId"?: string // Optional
    "tags": [
      {
        "id": string (uuid)
        "name": string
      }
    ]
  }
  }
  "inbox": { // Inbox summary embedded in a conversation payload.

``unread_conversations_count`` stays here because it is part of the
documented REST conversation API. It is an org-wide denormalized
counter, so WS emit paths must run the payload through
:func:`strip_org_wide_unread_count` before broadcasting it to rooms
that include members without ``GroupInbox.can_view_conversations``.
  {
    "id": string (uuid)
    "name": string
    "channelType": string (enum: line, telegram, teams, web, messenger, instagram, email, whatsapp, slack, team_plus, line_works, viber) // * `line` - LINE
* `telegram` - Telegram
* `teams` - Teams
* `web` - Web
* `messenger` - Messenger
* `instagram` - Instagram
* `email` - Email
* `whatsapp` - WhatsApp
* `slack` - Slack
* `team_plus` - Team+
* `line_works` - LINE WORKS
* `viber` - Viber
    "unreadConversationsCount"?: integer // Optional
    "signAuth"?:  // Optional
    {
      "id": string (uuid)
      "signSource": string (uuid)
      "signParams": {
      {
        "keycloak": 
        {
          "clientId": string
          "url": string
          "realm": string
        }
        "line": 
        {
          "liffId": string
        }
        "ad": 
        {
          "saml": string
        }
        "google"?:  // Optional
        {
          "enabled": boolean
        }
      }
      }
      "sourceIdAccessEnabled"?: boolean // When enabled, embedded visitors presenting a Source ID (verified per the verify mode below) may chat without going through the login flow. Disabled keeps every existing behavior unchanged. (Optional)
      "sourceIdVerifyMode"?:  // "Require signature verification": the embedding site must send an HMAC signature computed on its backend. "Source ID only": anyone knowing a Source ID can act as that contact; suitable only for testing or closed networks.

* `signature` - Require signature verification
* `raw` - Source ID only (Optional)
      {
      }
      "sourceIdSignatureTtlMinutes"?:  // How long a signature can be used to obtain the contact identity. Not a session lifetime: once exchanged, the session follows the existing WebChat mechanism.

* `1` - 1
* `5` - 5
* `15` - 15 (Optional)
      {
      }
      "keycloakTokenPassthrough"?: boolean // When enabled, the Keycloak access token a visitor logged in with is forwarded as the Authorization header when the AI calls MCP and API tools on their behalf, so tools do not require a separate per-tool login. Only for the Keycloak sign source. Never overrides an Authorization header a tool already resolves from its own credentials. The visitor identity is the organization member bound to the conversation at login, never the contact Source ID. (Optional)
      "keycloakTokenPassthroughAllowedHosts"?: [ // Hostnames (or "*.example.com" wildcards) of the tools the visitor Keycloak token may be sent to (Optional)
        string
      ]
      "organizationLogin": object // How visitors sign in when the sign source is "organization" (mirrors the back-office login); null for other sign sources
    }
    "enableAnalysis"?: boolean // Optional
  }
  }
  "chatbot": object // Chatbot (Agent) summary from the conversation's inbox, already eager-loaded.

``is_maigpt`` lets the client tell the default MaiGPT agent apart (icon, feature
gate) without comparing against the organization's ``maigpt_inbox`` id;
``file_workspace_mode`` is the effective P6 mode (MaiGPT, blueprint and the
organization's file-library switch already applied).
  "title": string
  "lastMessage"?: { // Optional
  {
    "id": string (uuid)
    "type"?: string // Optional
    "content"?: string // Optional
    "createdAt": string (timestamp)
  }
  }
  "callRecord": 
  {
    "id": string (uuid)
    "status"?: string (enum: initiated, ringing, in-progress, completed, failed, busy, no-answer, canceled) // * `initiated` - Initiated
* `ringing` - Ringing
* `in-progress` - In Progress
* `completed` - Completed
* `failed` - Failed
* `busy` - Busy
* `no-answer` - No Answer
* `canceled` - Canceled (Optional)
    "direction": string (enum: inbound, outbound) // * `inbound` - Inbound
* `outbound` - Outbound
    "isActive": boolean
    "canHangup": boolean
  }
  "lastMessageCreatedAt": string (timestamp)
  "unreadMessagesCount": integer
  "autoReplyEnabled": boolean
  "isAutoReplyNow": boolean
  "willAiReplyNow": boolean // Whether a text message arriving through the inbox gets an AI reply right now.

The messaging channels are gated by ``is_auto_reply_now`` (auto reply on and inside the
inbox auto-reply schedule) and by the inbox having a chatbot. The chatbot's API web chat
is answered by the completions endpoint, which ignores the schedule, so there the
conversation switch alone decides. The channel is only looked up for a web inbox outside
its schedule.

Surfaces that ignore the schedule on any inbox answer from their own rule instead: the
completions endpoint continuing an existing conversation (``completions_progress_status``)
and voice turns, which the voice agent answers.
  "lastReadAt": string (timestamp)
  "createdAt": string (timestamp)
  "isGroupChat": boolean
  "enableGroupMention"?: boolean // Optional
  "queryMetadata"?: { // Query metadata controlling the scope of searchable knowledge; see each endpoint for details (Optional)
  {
    "knowledgeBases"?: [ // List of searchable knowledge bases; an empty array or null denies all access (no permission), while omission imposes no restriction (Optional)
      {
        "knowledgeBaseId": string (uuid) // Knowledge base ID
        "chatbotFileIds"?: [ // List of file IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "knowledgeBaseFileIds"?: [ // List of file IDs (the new name for chatbotFileIds; this field takes precedence when both are provided) (Optional)
          string (uuid)
        ]
        "faqIds"?: [ // List of FAQ IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "hasUserSelectedAll"?: boolean // true selects all knowledge base content except the listed items; false selects only the listed items (Optional)
      }
    ]
    "labelRelations"?: { // Label filter criteria; an empty conditions array means no label filtering (not a denial of access). Must be provided together with knowledgeBases; when provided alone, knowledge base retrieval does not apply label filtering (no restriction) (Optional)
    {
      "operator": string (enum: AND, OR) // How label conditions are combined
      "conditions"?: [ // List of label conditions supporting nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "toolMask"?: object // Enabled/disabled state per tool: {tool_id: bool} (Optional)
  "connectorMask"?: object // Enabled/disabled state per connector: {connector_id: bool} (Optional)
  "thinkingConfig"?: object // None = not set (inherit the chatbot setting); otherwise {"reasoning_effort": <level>} (Optional)
  "thinkingConfigSchema": { // Thinking configuration form for this model; null means the model has no thinking configuration
  {
    "type"?: string // Form type, always reasoning_effort (Optional)
    "fields"?: object // reasoning_effort field: options lists the thinking levels accepted by this model (lowest to highest); default is always null (Optional)
    "canDisable"?: boolean // Whether thinking can be disabled for this model; when true, options includes off (Optional)
    "unsetBehavior"?: string (enum: off, model_default) // Actual behavior when neither the conversation nor the assistant sets a thinking level: off disables thinking (fastest); model_default lets the model decide or enables thinking at the platform default level (Optional)
    "unsetLevel"?: string // Level actually sent by the platform when no level is set; null means no thinking parameter is sent, leaving the model default in effect (Optional)
  }
  }
  "effectiveThinkingConfig": object // The level the next reply sends on this conversation's model, after gating and clamping.

The stored choice resolves conversation > chatbot > the LLM row's default level; None means
the model sends no thinking parameter, so the provider default applies.
  "llm": string (uuid) // The model the next reply runs on: the conversation's pin, else its assistant's current model.

TC-0205: returning the stored ``conversation.llm`` would show a model an unpinned conversation no
longer uses once its assistant switches model, while ``thinking_config_schema`` already follows the
effective model. Read-only: displaying it never marks the conversation as explicitly selected;
only the update-llm endpoint does.
  "deepResearchStatus":  // Status of deep research for this conversation

* `not_used` - Not Used
* `started` - Started
* `running` - Running
* `completed` - Completed
* `failed` - Failed
  {
  }
  "deepResearchOutputFormat": // May have different types
  string (enum: canvas, chat, file) // Chosen delivery form for the finished report. Set only when the user clicks a format in the pre-research choice card; reset to NULL each time Deep Research is (re-)armed. NULL means no explicit choice (or a plan-skipping request) and falls back to Canvas at render time, but stays distinct from an explicit canvas pick so the frontend can lock the card only after a real selection. To roll back to fixed-Canvas behavior, force this to NULL/canvas — no redeploy needed.

* `canvas` - Canvas
* `chat` - Chat reply
* `file` - Downloadable file
  "ipAddress": string
  "ipCountryCode": string
  "ipRecordedAt": string (timestamp)
  "assignee": 
  {
    "id": string (uuid)
    "name": string
    "userId": string (uuid)
  }
  "status"?: string (enum: open, resolved, queued) // * `open` - Open
* `resolved` - Resolved
* `queued` - Queued (Optional)
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "memberIds"?: [ // Optional
        string (uuid)
      ]
    }
  ]
  "progressStatus": string
  "firstResponseAt"?: string (timestamp) // Optional
  "queuedAt"?: string (timestamp) // Optional
  "assignedAt"?: string (timestamp) // Optional
  "transferReason"?: string // Optional
  "satisfactionScore": integer // Conversation satisfaction rating on a 5-point scale (1=very dissatisfied, 5=very satisfied)
  "satisfactionComment": string
  "satisfactionSubmittedAt": string (timestamp)
  "latestAnalysisSummary": string // The conversation's current analysis summary (its active summary revision).

Reads the `latest_analysis_summary` annotation added by `setup_eager_loading`
to avoid an N+1 query. All read paths (list / retrieve / create) eager-load,
so the annotation is present; it falls back to '' only for instances built
outside `setup_eager_loading`.
  "clientPlatform":  // Client platform that created this conversation (web, desktop)

* `web` - Web
* `desktop` - Desktop
  {
  }
  "workingDirectory": string // Absolute path of the local folder bound to a desktop conversation. Only set for desktop conversations; null for web conversations.
  "slideProjectId": string
  "matchedField": string // Cached per instance: ``get_matched_message_id`` / ``get_matched_snippet``
both read this, so resolving it once avoids recomputing the keyword and
title-match logic three times per serialized conversation.
  "matchedSnippet": string // Keyword-centred excerpt of the matched message, with ellipses.

Reads the ``matched_message_content`` annotation added by
``ConversationViewSet._annotate_keyword_match``; no extra query.
  "matchedMessageId": string
  "pinnedAt": string (timestamp) // When the conversation was pinned. Null means unpinned.
  "folder": string (uuid) // The user-defined sidebar folder this conversation is grouped into. Null means unclassified (shown under the time groups). Deleting the folder resets this to null without deleting the conversation.
  "hasActiveBrowserSession": boolean // Whether a browser_use session snapshot exists to resume.

Lets the frontend know it should proactively re-request the browser
panel (emit ``browser:start``) after a page reload, instead of only
ever showing the panel when a live ``browser:frame`` push happens to
arrive first.
  "callerOrganizationId": string // Bound caller organization (stamped at conversation start); null when unbound
  "handoff": object // Handoff information: enabled is the platform switch; the continuing conversation includes source_conversation_id/source_title; the original conversation includes continued_to_conversation_id/continued_to_title and latest_summary_message_id
  "displayContextUsage": boolean // Whether to display the context usage percentage bar (a conversation platform switch, always true for MaiGPT; independent of the handoff switch)
  "autoSplitReason": string // Reason this conversation was created by automatic splitting: resolved (after resolution) or idle (inactivity); null if not created by splitting
  "continuedFrom": object // Conversation this split conversation continues from: {id, last_message_at, deleted}; if the previous conversation was deleted, id=null and deleted=true
  "continuedTo": object // Most recent conversation split from this conversation: {id, created_at}; null if none
  "continuationSummary": object // Continuation summary: {state: disabled|pending|ready|not_applied, items: [{conversation_id, content, generated_at, last_message_at, old_status}]} (items ordered newest to oldest); null if not created by splitting
  "conversationKind": string // chat = a chat with an agent; secretary = the member's personal secretary Work chat
}
```

**Example Response Values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "contact": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "avatar": "Response string",
    "email": "response@example.com",
    "phoneNumber": "Response string",
    "metadata": null,
    "sourceId": "Response string",
    "tags": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string"
      }
    ]
  },
  "inbox": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "channelType": "line",
    "unreadConversationsCount": 456,
    "signAuth": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "signSource": "550e8400-e29b-41d4-a716-446655440000",
      "signParams": {
        "keycloak": {
          "clientId": "Response string",
          "url": "Response string",
          "realm": "Response string"
        },
        "line": {
          "liffId": "Response string"
        },
        "ad": {
          "saml": "Response string"
        },
        "google": {
          "enabled": false
        }
      },
      "sourceIdAccessEnabled": false,
      "sourceIdVerifyMode": {},
      "sourceIdSignatureTtlMinutes": {},
      "keycloakTokenPassthrough": false,
      "keycloakTokenPassthroughAllowedHosts": [
        "Response string"
      ],
      "organizationLogin": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      }
    },
    "enableAnalysis": false
  },
  "chatbot": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "title": "Response string",
  "lastMessage": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "Response string",
    "content": "Response string",
    "createdAt": "Response string"
  },
  "callRecord": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "initiated",
    "direction": "inbound",
    "isActive": false,
    "canHangup": false
  },
  "lastMessageCreatedAt": "Response string",
  "unreadMessagesCount": 456,
  "autoReplyEnabled": false,
  "isAutoReplyNow": false,
  "willAiReplyNow": false,
  "lastReadAt": "Response string",
  "createdAt": "Response string",
  "isGroupChat": false,
  "enableGroupMention": false,
  "queryMetadata": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "toolMask": null,
  "connectorMask": null,
  "thinkingConfig": null,
  "thinkingConfigSchema": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "effectiveThinkingConfig": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "llm": "550e8400-e29b-41d4-a716-446655440000",
  "deepResearchStatus": {},
  "deepResearchOutputFormat": "canvas",
  "ipAddress": "Response string",
  "ipCountryCode": "Response string",
  "ipRecordedAt": "Response string",
  "assignee": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "userId": "550e8400-e29b-41d4-a716-446655440000"
  },
  "status": "open",
  "tags": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string",
      "memberIds": [
        "550e8400-e29b-41d4-a716-446655440000"
      ]
    }
  ],
  "progressStatus": "Response string",
  "firstResponseAt": "Response string",
  "queuedAt": "Response string",
  "assignedAt": "Response string",
  "transferReason": "Response string",
  "satisfactionScore": 456,
  "satisfactionComment": "Response string",
  "satisfactionSubmittedAt": "Response string",
  "latestAnalysisSummary": "Response string",
  "clientPlatform": {},
  "workingDirectory": "Response string",
  "slideProjectId": "Response string",
  "matchedField": "Response string",
  "matchedSnippet": "Response string",
  "matchedMessageId": "Response string",
  "pinnedAt": "Response string",
  "folder": "550e8400-e29b-41d4-a716-446655440000",
  "hasActiveBrowserSession": false,
  "callerOrganizationId": "Response string",
  "handoff": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "displayContextUsage": false,
  "autoSplitReason": "Response string",
  "continuedFrom": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "continuedTo": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "continuationSummary": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "conversationKind": "Response string"
}
```

**Status Code: 4XX - Error response; X-Error-Code carries its code (see "Error codes").**

***

### Rename Conversation <a href="#conversations-rename-partial-update" id="conversations-rename-partial-update"></a>

PATCH `/api/v1/conversations/{id}/rename/`

#### Parameters

| Parameter Name | Required | Type   | Description                                  |
| -------------- | -------- | ------ | -------------------------------------------- |
| `id`           | ✅        | string | A UUID string identifying this Conversation. |

#### Request Body

**Request Parameters**

| Field | Type   | Required | Description |
| ----- | ------ | -------- | ----------- |
| title | string | No       |             |

**Example Request Schema**

```typescript
{
  "title"?: string // Optional
}
```

**Example Request Values**

```json
{
  "title": "Example name"
}
```

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X PATCH "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/rename/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Example name"
  }'

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
};

// Request Body (payload)
const data = {
    "title": "Example name"
  };

axios.patch("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/rename/", data, config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/rename/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request Body (payload)
data = {
    "title": "Example name"
}

response = requests.patch(url, json=data, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->patch("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/rename/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'title' => 'Example name'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 200**

**Example Response Schema**

```typescript
{
  "id": string (uuid)
  "contact": { // Lightweight Contact Serializer for the Conversation List view

Includes only the fields required by the List view, omitting inboxes, mcp_credentials and api_credentials
to avoid N+1 queries. email / phone_number / metadata are scalar fields on Contact itself,
needed for display in the frontend conversation sidebar, with no N+1 overhead.
  {
    "id": string (uuid)
    "name": string
    "avatar"?: string // Stores our own media as a relative storage key, or an external CDN URL as-is. (Optional)
    "email"?: // May have different types (Optional)
    string (email) // Optional
    "phoneNumber"?: string // Optional
    "metadata"?: object // Custom customer information shown in the customer panel of the conversation page (Optional)
    "sourceId"?: string // Optional
    "tags": [
      {
        "id": string (uuid)
        "name": string
      }
    ]
  }
  }
  "inbox": { // Inbox summary embedded in a conversation payload.

``unread_conversations_count`` stays here because it is part of the
documented REST conversation API. It is an org-wide denormalized
counter, so WS emit paths must run the payload through
:func:`strip_org_wide_unread_count` before broadcasting it to rooms
that include members without ``GroupInbox.can_view_conversations``.
  {
    "id": string (uuid)
    "name": string
    "channelType": string (enum: line, telegram, teams, web, messenger, instagram, email, whatsapp, slack, team_plus, line_works, viber) // * `line` - LINE
* `telegram` - Telegram
* `teams` - Teams
* `web` - Web
* `messenger` - Messenger
* `instagram` - Instagram
* `email` - Email
* `whatsapp` - WhatsApp
* `slack` - Slack
* `team_plus` - Team+
* `line_works` - LINE WORKS
* `viber` - Viber
    "unreadConversationsCount"?: integer // Optional
    "signAuth"?:  // Optional
    {
      "id": string (uuid)
      "signSource": string (uuid)
      "signParams": {
      {
        "keycloak": 
        {
          "clientId": string
          "url": string
          "realm": string
        }
        "line": 
        {
          "liffId": string
        }
        "ad": 
        {
          "saml": string
        }
        "google"?:  // Optional
        {
          "enabled": boolean
        }
      }
      }
      "sourceIdAccessEnabled"?: boolean // When enabled, embedded visitors presenting a Source ID (verified per the verify mode below) may chat without going through the login flow. Disabled keeps every existing behavior unchanged. (Optional)
      "sourceIdVerifyMode"?:  // "Require signature verification": the embedding site must send an HMAC signature computed on its backend. "Source ID only": anyone knowing a Source ID can act as that contact; suitable only for testing or closed networks.

* `signature` - Require signature verification
* `raw` - Source ID only (Optional)
      {
      }
      "sourceIdSignatureTtlMinutes"?:  // How long a signature can be used to obtain the contact identity. Not a session lifetime: once exchanged, the session follows the existing WebChat mechanism.

* `1` - 1
* `5` - 5
* `15` - 15 (Optional)
      {
      }
      "keycloakTokenPassthrough"?: boolean // When enabled, the Keycloak access token a visitor logged in with is forwarded as the Authorization header when the AI calls MCP and API tools on their behalf, so tools do not require a separate per-tool login. Only for the Keycloak sign source. Never overrides an Authorization header a tool already resolves from its own credentials. The visitor identity is the organization member bound to the conversation at login, never the contact Source ID. (Optional)
      "keycloakTokenPassthroughAllowedHosts"?: [ // Hostnames (or "*.example.com" wildcards) of the tools the visitor Keycloak token may be sent to (Optional)
        string
      ]
      "organizationLogin": object // How visitors sign in when the sign source is "organization" (mirrors the back-office login); null for other sign sources
    }
    "enableAnalysis"?: boolean // Optional
  }
  }
  "chatbot": object // Chatbot (Agent) summary from the conversation's inbox, already eager-loaded.

``is_maigpt`` lets the client tell the default MaiGPT agent apart (icon, feature
gate) without comparing against the organization's ``maigpt_inbox`` id;
``file_workspace_mode`` is the effective P6 mode (MaiGPT, blueprint and the
organization's file-library switch already applied).
  "title": string
  "lastMessage"?: { // Optional
  {
    "id": string (uuid)
    "type"?: string // Optional
    "content"?: string // Optional
    "createdAt": string (timestamp)
  }
  }
  "callRecord": 
  {
    "id": string (uuid)
    "status"?: string (enum: initiated, ringing, in-progress, completed, failed, busy, no-answer, canceled) // * `initiated` - Initiated
* `ringing` - Ringing
* `in-progress` - In Progress
* `completed` - Completed
* `failed` - Failed
* `busy` - Busy
* `no-answer` - No Answer
* `canceled` - Canceled (Optional)
    "direction": string (enum: inbound, outbound) // * `inbound` - Inbound
* `outbound` - Outbound
    "isActive": boolean
    "canHangup": boolean
  }
  "lastMessageCreatedAt": string (timestamp)
  "unreadMessagesCount": integer
  "autoReplyEnabled": boolean
  "isAutoReplyNow": boolean
  "willAiReplyNow": boolean // Whether a text message arriving through the inbox gets an AI reply right now.

The messaging channels are gated by ``is_auto_reply_now`` (auto reply on and inside the
inbox auto-reply schedule) and by the inbox having a chatbot. The chatbot's API web chat
is answered by the completions endpoint, which ignores the schedule, so there the
conversation switch alone decides. The channel is only looked up for a web inbox outside
its schedule.

Surfaces that ignore the schedule on any inbox answer from their own rule instead: the
completions endpoint continuing an existing conversation (``completions_progress_status``)
and voice turns, which the voice agent answers.
  "lastReadAt": string (timestamp)
  "createdAt": string (timestamp)
  "isGroupChat": boolean
  "enableGroupMention"?: boolean // Optional
  "queryMetadata"?: { // Query metadata controlling the scope of searchable knowledge; see each endpoint for details (Optional)
  {
    "knowledgeBases"?: [ // List of searchable knowledge bases; an empty array or null denies access to all knowledge bases (no permission), while omitting this field imposes no restriction (Optional)
      {
        "knowledgeBaseId": string (uuid) // Knowledge base ID
        "chatbotFileIds"?: [ // List of file IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "knowledgeBaseFileIds"?: [ // List of file IDs (the new name for chatbotFileIds; this field takes precedence when both are provided) (Optional)
          string (uuid)
        ]
        "faqIds"?: [ // List of FAQ IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "hasUserSelectedAll"?: boolean // true selects all knowledge base content except the listed items; false selects only the listed items (Optional)
      }
    ]
    "labelRelations"?: { // Label filter criteria; an empty conditions array disables label filtering (it does not deny access). Must be provided together with knowledgeBases; when provided alone, label filtering is not applied to knowledge base retrieval (no restriction) (Optional)
    {
      "operator": string (enum: AND, OR) // How label conditions are combined
      "conditions"?: [ // List of label conditions; supports nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "toolMask"?: object // Enabled/disabled state per tool: {tool_id: bool} (Optional)
  "connectorMask"?: object // Enabled/disabled state per connector: {connector_id: bool} (Optional)
  "thinkingConfig"?: object // None = not set (inherit the chatbot setting); otherwise {"reasoning_effort": <level>} (Optional)
  "thinkingConfigSchema": { // Thinking configuration form for this model; null means the model has no thinking settings
  {
    "type"?: string // Form type, always reasoning_effort (Optional)
    "fields"?: object // reasoning_effort field: options lists the thinking levels accepted by this model (from lowest to highest), and default is always null (Optional)
    "canDisable"?: boolean // Whether thinking can be disabled for this model; when true, options includes off (Optional)
    "unsetBehavior"?: string (enum: off, model_default) // Actual behavior when neither the conversation nor the assistant has a thinking level set: off disables thinking (fastest); model_default lets the model decide or enables thinking through the platform default level (Optional)
    "unsetLevel"?: string // Level the platform actually sends when no level is set; null means no thinking parameter is sent, leaving the model default in effect (Optional)
  }
  }
  "effectiveThinkingConfig": object // The level the next reply sends on this conversation's model, after gating and clamping.

The stored choice resolves conversation > chatbot > the LLM row's default level; None means
the model sends no thinking parameter, so the provider default applies.
  "llm": string (uuid) // The model the next reply runs on: the conversation's pin, else its assistant's current model.

TC-0205: returning the stored ``conversation.llm`` would show a model an unpinned conversation no
longer uses once its assistant switches model, while ``thinking_config_schema`` already follows the
effective model. Read-only: displaying it never marks the conversation as explicitly selected;
only the update-llm endpoint does.
  "deepResearchStatus":  // Status of deep research for this conversation

* `not_used` - Not Used
* `started` - Started
* `running` - Running
* `completed` - Completed
* `failed` - Failed
  {
  }
  "deepResearchOutputFormat": // May have different types
  string (enum: canvas, chat, file) // Chosen delivery form for the finished report. Set only when the user clicks a format in the pre-research choice card; reset to NULL each time Deep Research is (re-)armed. NULL means no explicit choice (or a plan-skipping request) and falls back to Canvas at render time, but stays distinct from an explicit canvas pick so the frontend can lock the card only after a real selection. To roll back to fixed-Canvas behavior, force this to NULL/canvas — no redeploy needed.

* `canvas` - Canvas
* `chat` - Chat reply
* `file` - Downloadable file
  "ipAddress": string
  "ipCountryCode": string
  "ipRecordedAt": string (timestamp)
  "assignee": 
  {
    "id": string (uuid)
    "name": string
    "userId": string (uuid)
  }
  "status"?: string (enum: open, resolved, queued) // * `open` - Open
* `resolved` - Resolved
* `queued` - Queued (Optional)
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "memberIds"?: [ // Optional
        string (uuid)
      ]
    }
  ]
  "progressStatus": string
  "firstResponseAt"?: string (timestamp) // Optional
  "queuedAt"?: string (timestamp) // Optional
  "assignedAt"?: string (timestamp) // Optional
  "transferReason"?: string // Optional
  "satisfactionScore": integer // Conversation satisfaction rating on a 5-point scale (1=very dissatisfied, 5=very satisfied)
  "satisfactionComment": string
  "satisfactionSubmittedAt": string (timestamp)
  "latestAnalysisSummary": string // The conversation's current analysis summary (its active summary revision).

Reads the `latest_analysis_summary` annotation added by `setup_eager_loading`
to avoid an N+1 query. All read paths (list / retrieve / create) eager-load,
so the annotation is present; it falls back to '' only for instances built
outside `setup_eager_loading`.
  "clientPlatform":  // Client platform that created this conversation (web, desktop)

* `web` - Web
* `desktop` - Desktop
  {
  }
  "workingDirectory": string // Absolute path of the local folder bound to a desktop conversation. Only set for desktop conversations; null for web conversations.
  "slideProjectId": string
  "matchedField": string // Cached per instance: ``get_matched_message_id`` / ``get_matched_snippet``
both read this, so resolving it once avoids recomputing the keyword and
title-match logic three times per serialized conversation.
  "matchedSnippet": string // Keyword-centred excerpt of the matched message, with ellipses.

Reads the ``matched_message_content`` annotation added by
``ConversationViewSet._annotate_keyword_match``; no extra query.
  "matchedMessageId": string
  "pinnedAt": string (timestamp) // When the conversation was pinned. Null means unpinned.
  "folder": string (uuid) // The user-defined sidebar folder this conversation is grouped into. Null means unclassified (shown under the time groups). Deleting the folder resets this to null without deleting the conversation.
  "hasActiveBrowserSession": boolean // Whether a browser_use session snapshot exists to resume.

Lets the frontend know it should proactively re-request the browser
panel (emit ``browser:start``) after a page reload, instead of only
ever showing the panel when a live ``browser:frame`` push happens to
arrive first.
  "callerOrganizationId": string // Bound caller organization (stamped at conversation start); null when unbound
  "handoff": object // Handoff information: enabled is the platform toggle; the continuing conversation includes source_conversation_id/source_title; the original conversation being handed off includes continued_to_conversation_id/continued_to_title and latest_summary_message_id
  "displayContextUsage": boolean // Whether to display the context usage percentage bar (a conversation platform toggle, always true for MaiGPT; independent of the handoff toggle)
  "autoSplitReason": string // Reason this conversation was created by automatic conversation splitting: resolved (after resolution) or idle (inactivity); null if not created by splitting
  "continuedFrom": object // Conversation from which this split conversation continues: {id, last_message_at, deleted}; if the previous conversation was deleted, id=null and deleted=true
  "continuedTo": object // Most recent conversation split from this conversation: {id, created_at}; null if none
  "continuationSummary": object // Continuation summary: {state: disabled|pending|ready|not_applied, items: [{conversation_id, content, generated_at, last_message_at, old_status}]} (items ordered newest to oldest); null if not created by splitting
  "conversationKind": string // chat = a chat with an agent; secretary = the member's personal secretary Work chat
}
```

**Example Response Values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "contact": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "avatar": "Response string",
    "email": "response@example.com",
    "phoneNumber": "Response string",
    "metadata": null,
    "sourceId": "Response string",
    "tags": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string"
      }
    ]
  },
  "inbox": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "channelType": "line",
    "unreadConversationsCount": 456,
    "signAuth": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "signSource": "550e8400-e29b-41d4-a716-446655440000",
      "signParams": {
        "keycloak": {
          "clientId": "Response string",
          "url": "Response string",
          "realm": "Response string"
        },
        "line": {
          "liffId": "Response string"
        },
        "ad": {
          "saml": "Response string"
        },
        "google": {
          "enabled": false
        }
      },
      "sourceIdAccessEnabled": false,
      "sourceIdVerifyMode": {},
      "sourceIdSignatureTtlMinutes": {},
      "keycloakTokenPassthrough": false,
      "keycloakTokenPassthroughAllowedHosts": [
        "Response string"
      ],
      "organizationLogin": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      }
    },
    "enableAnalysis": false
  },
  "chatbot": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "title": "Response string",
  "lastMessage": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "Response string",
    "content": "Response string",
    "createdAt": "Response string"
  },
  "callRecord": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "initiated",
    "direction": "inbound",
    "isActive": false,
    "canHangup": false
  },
  "lastMessageCreatedAt": "Response string",
  "unreadMessagesCount": 456,
  "autoReplyEnabled": false,
  "isAutoReplyNow": false,
  "willAiReplyNow": false,
  "lastReadAt": "Response string",
  "createdAt": "Response string",
  "isGroupChat": false,
  "enableGroupMention": false,
  "queryMetadata": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "toolMask": null,
  "connectorMask": null,
  "thinkingConfig": null,
  "thinkingConfigSchema": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "effectiveThinkingConfig": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "llm": "550e8400-e29b-41d4-a716-446655440000",
  "deepResearchStatus": {},
  "deepResearchOutputFormat": "canvas",
  "ipAddress": "Response string",
  "ipCountryCode": "Response string",
  "ipRecordedAt": "Response string",
  "assignee": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "userId": "550e8400-e29b-41d4-a716-446655440000"
  },
  "status": "open",
  "tags": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string",
      "memberIds": [
        "550e8400-e29b-41d4-a716-446655440000"
      ]
    }
  ],
  "progressStatus": "Response string",
  "firstResponseAt": "Response string",
  "queuedAt": "Response string",
  "assignedAt": "Response string",
  "transferReason": "Response string",
  "satisfactionScore": 456,
  "satisfactionComment": "Response string",
  "satisfactionSubmittedAt": "Response string",
  "latestAnalysisSummary": "Response string",
  "clientPlatform": {},
  "workingDirectory": "Response string",
  "slideProjectId": "Response string",
  "matchedField": "Response string",
  "matchedSnippet": "Response string",
  "matchedMessageId": "Response string",
  "pinnedAt": "Response string",
  "folder": "550e8400-e29b-41d4-a716-446655440000",
  "hasActiveBrowserSession": false,
  "callerOrganizationId": "Response string",
  "handoff": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "displayContextUsage": false,
  "autoSplitReason": "Response string",
  "continuedFrom": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "continuedTo": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "continuationSummary": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "conversationKind": "Response string"
}
```

**Status Code: 4XX - Error response; X-Error-Code carries its code (see "Error codes").**

***

### Delete Conversation <a href="#conversations-destroy" id="conversations-destroy"></a>

DELETE `/api/v1/conversations/{id}/`

#### Parameters

| Parameter Name | Required | Type   | Description                                  |
| -------------- | -------- | ------ | -------------------------------------------- |
| `id`           | ✅        | string | A UUID string identifying this Conversation. |

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X DELETE "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/" \
  -H "Authorization: Api-Key YOUR_API_KEY"

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY'
  }
};

// Request body (payload)
const data = null;

axios.delete("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/", data, config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request failed:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY"
}


response = requests.delete(url, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request failed:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->delete("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request failed: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

| Status Code | Description                                                        |
| ----------- | ------------------------------------------------------------------ |
| 204         | No response body                                                   |
| 4XX         | Error response; X-Error-Code carries its code (see "Error codes"). |

***

### Stop AI Response <a href="#conversations-stop-response-create" id="conversations-stop-response-create"></a>

POST `/api/v1/conversations/{conversationId}/stop-response/`

#### Parameters

| Parameter Name   | Required | Type   | Description |
| ---------------- | -------- | ------ | ----------- |
| `conversationId` | ✅        | string |             |

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X POST "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/stop-response/" \
  -H "Authorization: Api-Key YOUR_API_KEY"

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY'
  }
};

// Request body (payload)
const data = null;

axios.post("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/stop-response/", data, config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request failed:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/stop-response/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY"
}


response = requests.post(url, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request failed:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->post("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/stop-response/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request failed: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

| Status Code | Description                                                        |
| ----------- | ------------------------------------------------------------------ |
| 200         | No response body                                                   |
| 4XX         | Error response; X-Error-Code carries its code (see "Error codes"). |

***

### Submit Conversation Satisfaction <a href="#conversations-satisfaction-create" id="conversations-satisfaction-create"></a>

POST `/api/v1/conversations/{id}/satisfaction/`

{% hint style="info" %}
Satisfaction can only be submitted once per conversation.
{% endhint %}

#### Parameters

| Parameter Name | Required | Type   | Description                                  |
| -------------- | -------- | ------ | -------------------------------------------- |
| `id`           | ✅        | string | A UUID string identifying this Conversation. |

#### Request Body

**Request Parameters**

| Field               | Type    | Required | Description                                                                                 |
| ------------------- | ------- | -------- | ------------------------------------------------------------------------------------------- |
| satisfactionScore   | integer | Yes      | Conversation satisfaction rating on a 5-point scale (1=very dissatisfied, 5=very satisfied) |
| satisfactionComment | string  | No       |                                                                                             |

**Request Schema Example**

```typescript
{
  "satisfactionScore": integer // Conversation satisfaction rating on a 5-point scale (1=very dissatisfied, 5=very satisfied)
  "satisfactionComment"?: string // Optional
}
```

**Example Request Values**

```json
{
  "satisfactionScore": 5,
  "satisfactionComment": "The answer was clear and the issue has been resolved"
}
```

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API Call Example (Shell)
curl -X POST "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/satisfaction/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "satisfactionScore": 5,
    "satisfactionComment": "The answer was clear and the issue has been resolved"
  }'

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
};

// Request Body (payload)
const data = {
    "satisfactionScore": 5,
    "satisfactionComment": "The answer was clear and the issue has been resolved"
  };

axios.post("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/satisfaction/", data, config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/satisfaction/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request Body (payload)
data = {
    "satisfactionScore": 5,
    "satisfactionComment": "The answer was clear and the issue has been resolved"
}

response = requests.post(url, json=data, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->post("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/satisfaction/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'satisfactionScore' => 5,
            'satisfactionComment' => 'The answer was clear and the issue has been resolved'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 201**

**Response Schema Example**

```typescript
{
  "satisfactionScore": integer
  "satisfactionComment"?: string // Optional
  "satisfactionSubmittedAt": string // Submission time as an ISO 8601 UTC string, for example 2026-10-09T15:10:48.717193Z
}
```

**Example Response Values**

```json
{
  "satisfactionScore": 456,
  "satisfactionComment": "Response string",
  "satisfactionSubmittedAt": "Response string"
}
```

**Status Code: 409 - Satisfaction has already been submitted for this conversation**

***

### List Conversation Events <a href="#conversations-events-retrieve" id="conversations-events-retrieve"></a>

GET `/api/v1/conversations/{id}/events/`

{% hint style="info" %}
Event records for the human handoff workflow (`event_type`: `transfer_triggered`／`human_assigned`／`returned_to_ai`／`queue_timeout`), which can be reconciled with Webhooks. For the workflow, see [Customer Service System Integration](https://docs.maiagent.ai/tech/api-integration/customer-service-system-integration) in the technical manual.
{% endhint %}

#### Parameters

| Parameter Name | Required | Type   | Description                                  |
| -------------- | -------- | ------ | -------------------------------------------- |
| `id`           | ✅        | string | A UUID string identifying this Conversation. |

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API Call Example (Shell)
curl -X GET "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/events/" \
  -H "Authorization: Api-Key YOUR_API_KEY"

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY'
  }
};

axios.get("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/events/", config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/events/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY"
}


response = requests.get(url, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->get("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/events/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 200**

**Response Schema Example**

```typescript
{
  "id": string (uuid)
  "contact": { // Lightweight Contact Serializer for the Conversation List view

Includes only the fields required by the List view, omitting inboxes, mcp_credentials, and api_credentials
to avoid N+1 queries. email / phone_number / metadata are scalar fields on Contact itself,
needed for display in the frontend conversation sidebar, with no N+1 overhead.
  {
    "id": string (uuid)
    "name": string
    "avatar"?: string // Stores our own media as a relative storage key, or an external CDN URL as-is. (Optional)
    "email"?: // May have different types (Optional)
    string (email) // Optional
    "phoneNumber"?: string // Optional
    "metadata"?: object // Custom customer information shown in the customer panel of the conversation page (Optional)
    "sourceId"?: string // Optional
    "tags": [
      {
        "id": string (uuid)
        "name": string
      }
    ]
  }
  }
  "inbox": { // Inbox summary embedded in a conversation payload.

``unread_conversations_count`` stays here because it is part of the
documented REST conversation API. It is an org-wide denormalized
counter, so WS emit paths must run the payload through
:func:`strip_org_wide_unread_count` before broadcasting it to rooms
that include members without ``GroupInbox.can_view_conversations``.
  {
    "id": string (uuid)
    "name": string
    "channelType": string (enum: line, telegram, teams, web, messenger, instagram, email, whatsapp, slack, team_plus, line_works, viber) // * `line` - LINE
* `telegram` - Telegram
* `teams` - Teams
* `web` - Web
* `messenger` - Messenger
* `instagram` - Instagram
* `email` - Email
* `whatsapp` - WhatsApp
* `slack` - Slack
* `team_plus` - Team+
* `line_works` - LINE WORKS
* `viber` - Viber
    "unreadConversationsCount"?: integer // Optional
    "signAuth"?:  // Optional
    {
      "id": string (uuid)
      "signSource": string (uuid)
      "signParams": {
      {
        "keycloak": 
        {
          "clientId": string
          "url": string
          "realm": string
        }
        "line": 
        {
          "liffId": string
        }
        "ad": 
        {
          "saml": string
        }
        "google"?:  // Optional
        {
          "enabled": boolean
        }
      }
      }
      "sourceIdAccessEnabled"?: boolean // When enabled, embedded visitors presenting a Source ID (verified per the verify mode below) may chat without going through the login flow. Disabled keeps every existing behavior unchanged. (Optional)
      "sourceIdVerifyMode"?:  // "Require signature verification": the embedding site must send an HMAC signature computed on its backend. "Source ID only": anyone knowing a Source ID can act as that contact; suitable only for testing or closed networks.

* `signature` - Require signature verification
* `raw` - Source ID only (Optional)
      {
      }
      "sourceIdSignatureTtlMinutes"?:  // How long a signature can be used to obtain the contact identity. Not a session lifetime: once exchanged, the session follows the existing WebChat mechanism.

* `1` - 1
* `5` - 5
* `15` - 15 (Optional)
      {
      }
      "keycloakTokenPassthrough"?: boolean // When enabled, the Keycloak access token a visitor logged in with is forwarded as the Authorization header when the AI calls MCP and API tools on their behalf, so tools do not require a separate per-tool login. Only for the Keycloak sign source. Never overrides an Authorization header a tool already resolves from its own credentials. The visitor identity is the organization member bound to the conversation at login, never the contact Source ID. (Optional)
      "keycloakTokenPassthroughAllowedHosts"?: [ // Hostnames (or "*.example.com" wildcards) of the tools the visitor Keycloak token may be sent to (Optional)
        string
      ]
      "organizationLogin": object // How visitors sign in when the sign source is "organization" (mirrors the back-office login); null for other sign sources
    }
    "enableAnalysis"?: boolean // Optional
  }
  }
  "chatbot": object // Chatbot (Agent) summary from the conversation's inbox, already eager-loaded.

``is_maigpt`` lets the client tell the default MaiGPT agent apart (icon, feature
gate) without comparing against the organization's ``maigpt_inbox`` id;
``file_workspace_mode`` is the effective P6 mode (MaiGPT, blueprint and the
organization's file-library switch already applied).
  "title": string
  "lastMessage"?: { // Optional
  {
    "id": string (uuid)
    "type"?: string // Optional
    "content"?: string // Optional
    "createdAt": string (timestamp)
  }
  }
  "callRecord": 
  {
    "id": string (uuid)
    "status"?: string (enum: initiated, ringing, in-progress, completed, failed, busy, no-answer, canceled) // * `initiated` - Initiated
* `ringing` - Ringing
* `in-progress` - In Progress
* `completed` - Completed
* `failed` - Failed
* `busy` - Busy
* `no-answer` - No Answer
* `canceled` - Canceled (Optional)
    "direction": string (enum: inbound, outbound) // * `inbound` - Inbound
* `outbound` - Outbound
    "isActive": boolean
    "canHangup": boolean
  }
  "lastMessageCreatedAt": string (timestamp)
  "unreadMessagesCount": integer
  "autoReplyEnabled": boolean
  "isAutoReplyNow": boolean
  "willAiReplyNow": boolean // Whether a text message arriving through the inbox gets an AI reply right now.

The messaging channels are gated by ``is_auto_reply_now`` (auto reply on and inside the
inbox auto-reply schedule) and by the inbox having a chatbot. The chatbot's API web chat
is answered by the completions endpoint, which ignores the schedule, so there the
conversation switch alone decides. The channel is only looked up for a web inbox outside
its schedule.

Surfaces that ignore the schedule on any inbox answer from their own rule instead: the
completions endpoint continuing an existing conversation (``completions_progress_status``)
and voice turns, which the voice agent answers.
  "lastReadAt": string (timestamp)
  "createdAt": string (timestamp)
  "isGroupChat": boolean
  "enableGroupMention"?: boolean // Optional
  "queryMetadata"?: { // Query metadata controlling the scope of accessible knowledge; see each endpoint for details (Optional)
  {
    "knowledgeBases"?: [ // List of accessible knowledge bases; an empty array or null means none are accessible (no permission); omitted means unrestricted (Optional)
      {
        "knowledgeBaseId": string (uuid) // Knowledge base ID
        "chatbotFileIds"?: [ // List of file IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "knowledgeBaseFileIds"?: [ // List of file IDs (the new name for chatbotFileIds; this field takes precedence when both are provided) (Optional)
          string (uuid)
        ]
        "faqIds"?: [ // List of FAQ IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "hasUserSelectedAll"?: boolean // true = select all knowledge base content except the listed items; false = select only the listed items (Optional)
      }
    ]
    "labelRelations"?: { // Tag filter criteria; an empty conditions array means no tag filtering (not a lack of permission). Must be provided with knowledgeBases; when supplied alone, tag filtering is not applied to knowledge base retrieval (equivalent to unrestricted access) (Optional)
    {
      "operator": string (enum: AND, OR) // How tag conditions are combined
      "conditions"?: [ // List of tag conditions supporting nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "toolMask"?: object // Enabled/disabled state per tool: {tool_id: bool} (Optional)
  "connectorMask"?: object // Enabled/disabled state per connector: {connector_id: bool} (Optional)
  "thinkingConfig"?: object // None = not set (inherit the chatbot setting); otherwise {"reasoning_effort": <level>} (Optional)
  "thinkingConfigSchema": { // Thinking configuration form for this model; null means the model has no thinking settings
  {
    "type"?: string // Form type, always reasoning_effort (Optional)
    "fields"?: object // reasoning_effort field: options lists the thinking levels accepted by this model (from lowest to highest); default is always null (Optional)
    "canDisable"?: boolean // Whether thinking can be disabled for this model; when true, options includes off (Optional)
    "unsetBehavior"?: string (enum: off, model_default) // Behavior when neither the conversation nor the assistant specifies a thinking level: off = no thinking (fastest); model_default = the model decides, or the platform default level enables thinking (Optional)
    "unsetLevel"?: string // Level actually sent by the platform when no level is set; null means no thinking parameter is sent, leaving the model default in effect (Optional)
  }
  }
  "effectiveThinkingConfig": object // The level the next reply sends on this conversation's model, after gating and clamping.

The stored choice resolves conversation > chatbot > the LLM row's default level; None means
the model sends no thinking parameter, so the provider default applies.
  "llm": string (uuid) // The model the next reply runs on: the conversation's pin, else its assistant's current model.

TC-0205: returning the stored ``conversation.llm`` would show a model an unpinned conversation no
longer uses once its assistant switches model, while ``thinking_config_schema`` already follows the
effective model. Read-only: displaying it never marks the conversation as explicitly selected;
only the update-llm endpoint does.
  "deepResearchStatus":  // Status of deep research for this conversation

* `not_used` - Not Used
* `started` - Started
* `running` - Running
* `completed` - Completed
* `failed` - Failed
  {
  }
  "deepResearchOutputFormat": // May have different types
  string (enum: canvas, chat, file) // Chosen delivery form for the finished report. Set only when the user clicks a format in the pre-research choice card; reset to NULL each time Deep Research is (re-)armed. NULL means no explicit choice (or a plan-skipping request) and falls back to Canvas at render time, but stays distinct from an explicit canvas pick so the frontend can lock the card only after a real selection. To roll back to fixed-Canvas behavior, force this to NULL/canvas — no redeploy needed.

* `canvas` - Canvas
* `chat` - Chat reply
* `file` - Downloadable file
  "ipAddress": string
  "ipCountryCode": string
  "ipRecordedAt": string (timestamp)
  "assignee": 
  {
    "id": string (uuid)
    "name": string
    "userId": string (uuid)
  }
  "status"?: string (enum: open, resolved, queued) // * `open` - Open
* `resolved` - Resolved
* `queued` - Queued (Optional)
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "memberIds"?: [ // Optional
        string (uuid)
      ]
    }
  ]
  "progressStatus": string
  "firstResponseAt"?: string (timestamp) // Optional
  "queuedAt"?: string (timestamp) // Optional
  "assignedAt"?: string (timestamp) // Optional
  "transferReason"?: string // Optional
  "satisfactionScore": integer // Conversation satisfaction rating on a 5-point scale (1=very dissatisfied, 5=very satisfied)
  "satisfactionComment": string
  "satisfactionSubmittedAt": string (timestamp)
  "latestAnalysisSummary": string // The conversation's current analysis summary (its active summary revision).

Reads the `latest_analysis_summary` annotation added by `setup_eager_loading`
to avoid an N+1 query. All read paths (list / retrieve / create) eager-load,
so the annotation is present; it falls back to '' only for instances built
outside `setup_eager_loading`.
  "clientPlatform":  // Client platform that created this conversation (web, desktop)

* `web` - Web
* `desktop` - Desktop
  {
  }
  "workingDirectory": string // Absolute path of the local folder bound to a desktop conversation. Only set for desktop conversations; null for web conversations.
  "slideProjectId": string
  "matchedField": string // Cached per instance: ``get_matched_message_id`` / ``get_matched_snippet``
both read this, so resolving it once avoids recomputing the keyword and
title-match logic three times per serialized conversation.
  "matchedSnippet": string // Keyword-centred excerpt of the matched message, with ellipses.

Reads the ``matched_message_content`` annotation added by
``ConversationViewSet._annotate_keyword_match``; no extra query.
  "matchedMessageId": string
  "pinnedAt": string (timestamp) // When the conversation was pinned. Null means unpinned.
  "folder": string (uuid) // The user-defined sidebar folder this conversation is grouped into. Null means unclassified (shown under the time groups). Deleting the folder resets this to null without deleting the conversation.
  "hasActiveBrowserSession": boolean // Whether a browser_use session snapshot exists to resume.

Lets the frontend know it should proactively re-request the browser
panel (emit ``browser:start``) after a page reload, instead of only
ever showing the panel when a live ``browser:frame`` push happens to
arrive first.
  "callerOrganizationId": string // Bound caller organization (stamped at conversation start); null when unbound
  "handoff": object // Handoff information: enabled = platform switch; the continuing conversation includes source_conversation_id/source_title; the original conversation being handed off includes continued_to_conversation_id/continued_to_title and latest_summary_message_id
  "displayContextUsage": boolean // Whether to display the context usage percentage bar (conversation platform switch, always true for MaiGPT; independent of the handoff switch)
  "autoSplitReason": string // Reason this conversation was created by automatic conversation splitting: resolved (after resolution)／idle (inactivity); null if not created by splitting
  "continuedFrom": object // Conversation from which this split conversation continues: {id, last_message_at, deleted}; if the previous conversation was deleted, id=null and deleted=true
  "continuedTo": object // Most recent conversation split off from this conversation: {id, created_at}; null if none
  "continuationSummary": object // Continuation summary: {state: disabled|pending|ready|not_applied, items: [{conversation_id, content, generated_at, last_message_at, old_status}]} (items ordered newest to oldest); null if not created by splitting
  "conversationKind": string // chat = a chat with an agent; secretary = the member's personal secretary Work chat
}
```

**Example Response Values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "contact": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "avatar": "Response string",
    "email": "response@example.com",
    "phoneNumber": "Response string",
    "metadata": null,
    "sourceId": "Response string",
    "tags": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string"
      }
    ]
  },
  "inbox": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "channelType": "line",
    "unreadConversationsCount": 456,
    "signAuth": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "signSource": "550e8400-e29b-41d4-a716-446655440000",
      "signParams": {
        "keycloak": {
          "clientId": "Response string",
          "url": "Response string",
          "realm": "Response string"
        },
        "line": {
          "liffId": "Response string"
        },
        "ad": {
          "saml": "Response string"
        },
        "google": {
          "enabled": false
        }
      },
      "sourceIdAccessEnabled": false,
      "sourceIdVerifyMode": {},
      "sourceIdSignatureTtlMinutes": {},
      "keycloakTokenPassthrough": false,
      "keycloakTokenPassthroughAllowedHosts": [
        "Response string"
      ],
      "organizationLogin": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example Response Name",
        "description": "Example Response Description"
      }
    },
    "enableAnalysis": false
  },
  "chatbot": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example Response Name",
    "description": "Example Response Description"
  },
  "title": "Response string",
  "lastMessage": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "Response string",
    "content": "Response string",
    "createdAt": "Response string"
  },
  "callRecord": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "initiated",
    "direction": "inbound",
    "isActive": false,
    "canHangup": false
  },
  "lastMessageCreatedAt": "Response string",
  "unreadMessagesCount": 456,
  "autoReplyEnabled": false,
  "isAutoReplyNow": false,
  "willAiReplyNow": false,
  "lastReadAt": "Response string",
  "createdAt": "Response string",
  "isGroupChat": false,
  "enableGroupMention": false,
  "queryMetadata": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example Response Name",
    "description": "Example Response Description"
  },
  "toolMask": null,
  "connectorMask": null,
  "thinkingConfig": null,
  "thinkingConfigSchema": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example Response Name",
    "description": "Example Response Description"
  },
  "effectiveThinkingConfig": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example Response Name",
    "description": "Example Response Description"
  },
  "llm": "550e8400-e29b-41d4-a716-446655440000",
  "deepResearchStatus": {},
  "deepResearchOutputFormat": "canvas",
  "ipAddress": "Response string",
  "ipCountryCode": "Response string",
  "ipRecordedAt": "Response string",
  "assignee": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "userId": "550e8400-e29b-41d4-a716-446655440000"
  },
  "status": "open",
  "tags": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string",
      "memberIds": [
        "550e8400-e29b-41d4-a716-446655440000"
      ]
    }
  ],
  "progressStatus": "Response string",
  "firstResponseAt": "Response string",
  "queuedAt": "Response string",
  "assignedAt": "Response string",
  "transferReason": "Response string",
  "satisfactionScore": 456,
  "satisfactionComment": "Response string",
  "satisfactionSubmittedAt": "Response string",
  "latestAnalysisSummary": "Response string",
  "clientPlatform": {},
  "workingDirectory": "Response string",
  "slideProjectId": "Response string",
  "matchedField": "Response string",
  "matchedSnippet": "Response string",
  "matchedMessageId": "Response string",
  "pinnedAt": "Response string",
  "folder": "550e8400-e29b-41d4-a716-446655440000",
  "hasActiveBrowserSession": false,
  "callerOrganizationId": "Response string",
  "handoff": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example Response Name",
    "description": "Example Response Description"
  },
  "displayContextUsage": false,
  "autoSplitReason": "Response string",
  "continuedFrom": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example Response Name",
    "description": "Example Response Description"
  },
  "continuedTo": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example Response Name",
    "description": "Example Response Description"
  },
  "continuationSummary": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example Response Name",
    "description": "Example Response Description"
  },
  "conversationKind": "Response string"
}
```

**Status Code: 4XX - Error response; X-Error-Code carries its code (see "Error codes").**

***

### Assign Conversation to a Member <a href="#conversations-assign-partial-update" id="conversations-assign-partial-update"></a>

PATCH `/api/v1/conversations/{id}/assign/`

{% hint style="info" %}
Set the member taking over and disable AI auto-reply; queued conversations move to in progress.
{% endhint %}

#### Parameters

| Parameter Name | Required | Type   | Description                                  |
| -------------- | -------- | ------ | -------------------------------------------- |
| `id`           | ✅        | string | A UUID string identifying this Conversation. |

#### Request Body

**Request Parameters**

| Field          | Type           | Required | Description |
| -------------- | -------------- | -------- | ----------- |
| assignee       | string (uuid)  | No       |             |
| transferReason | string         | No       |             |
| tagIds         | array\[string] | No       |             |

**Request Schema Example**

```typescript
{
  "assignee"?: string (uuid) // Optional
  "transferReason"?: string // Optional
  "tagIds"?: [ // Optional
    string (uuid)
  ]
}
```

**Example Request Values**

```json
{
  "assignee": "550e8400-e29b-41d4-a716-446655440000",
  "transferReason": "Example string",
  "tagIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ]
}
```

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X PATCH "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/assign/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "assignee": "550e8400-e29b-41d4-a716-446655440000",
    "transferReason": "Example string",
    "tagIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ]
  }'

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
};

// Request body (payload)
const data = {
    "assignee": "550e8400-e29b-41d4-a716-446655440000",
    "transferReason": "Example string",
    "tagIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ]
  };

axios.patch("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/assign/", data, config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request failed:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/assign/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request body (payload)
data = {
    "assignee": "550e8400-e29b-41d4-a716-446655440000",
    "transferReason": "Example string",
    "tagIds": [
        "550e8400-e29b-41d4-a716-446655440000"
    ]
}

response = requests.patch(url, json=data, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request failed:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->patch("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/assign/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'assignee' => '550e8400-e29b-41d4-a716-446655440000',
            'transferReason' => 'Example string',
            'tagIds' => [
                '550e8400-e29b-41d4-a716-446655440000'
            ]
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request failed: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status code: 200**

**Example Response Schema**

```typescript
{
  "id": string (uuid)
  "contact": { // Lightweight Contact Serializer for the Conversation List view

Includes only the fields required for the List view; omits inboxes, mcp_credentials, and api_credentials
to avoid N+1 queries. email / phone_number / metadata are scalar fields on Contact itself,
needed for display in the frontend conversation sidebar, with no N+1 overhead.
  {
    "id": string (uuid)
    "name": string
    "avatar"?: string // Stores our own media as a relative storage key, or an external CDN URL as-is. (Optional)
    "email"?: // May have different types (Optional)
    string (email) // Optional
    "phoneNumber"?: string // Optional
    "metadata"?: object // Custom customer information shown in the customer panel of the conversation page (Optional)
    "sourceId"?: string // Optional
    "tags": [
      {
        "id": string (uuid)
        "name": string
      }
    ]
  }
  }
  "inbox": { // Inbox summary embedded in a conversation payload.

``unread_conversations_count`` stays here because it is part of the
documented REST conversation API. It is an org-wide denormalized
counter, so WS emit paths must run the payload through
:func:`strip_org_wide_unread_count` before broadcasting it to rooms
that include members without ``GroupInbox.can_view_conversations``.
  {
    "id": string (uuid)
    "name": string
    "channelType": string (enum: line, telegram, teams, web, messenger, instagram, email, whatsapp, slack, team_plus, line_works, viber) // * `line` - LINE
* `telegram` - Telegram
* `teams` - Teams
* `web` - Web
* `messenger` - Messenger
* `instagram` - Instagram
* `email` - Email
* `whatsapp` - WhatsApp
* `slack` - Slack
* `team_plus` - Team+
* `line_works` - LINE WORKS
* `viber` - Viber
    "unreadConversationsCount"?: integer // Optional
    "signAuth"?:  // Optional
    {
      "id": string (uuid)
      "signSource": string (uuid)
      "signParams": {
      {
        "keycloak": 
        {
          "clientId": string
          "url": string
          "realm": string
        }
        "line": 
        {
          "liffId": string
        }
        "ad": 
        {
          "saml": string
        }
        "google"?:  // Optional
        {
          "enabled": boolean
        }
      }
      }
      "sourceIdAccessEnabled"?: boolean // When enabled, embedded visitors presenting a Source ID (verified per the verify mode below) may chat without going through the login flow. Disabled keeps every existing behavior unchanged. (Optional)
      "sourceIdVerifyMode"?:  // "Require signature verification": the embedding site must send an HMAC signature computed on its backend. "Source ID only": anyone knowing a Source ID can act as that contact; suitable only for testing or closed networks.

* `signature` - Require signature verification
* `raw` - Source ID only (Optional)
      {
      }
      "sourceIdSignatureTtlMinutes"?:  // How long a signature can be used to obtain the contact identity. Not a session lifetime: once exchanged, the session follows the existing WebChat mechanism.

* `1` - 1
* `5` - 5
* `15` - 15 (Optional)
      {
      }
      "keycloakTokenPassthrough"?: boolean // When enabled, the Keycloak access token a visitor logged in with is forwarded as the Authorization header when the AI calls MCP and API tools on their behalf, so tools do not require a separate per-tool login. Only for the Keycloak sign source. Never overrides an Authorization header a tool already resolves from its own credentials. The visitor identity is the organization member bound to the conversation at login, never the contact Source ID. (Optional)
      "keycloakTokenPassthroughAllowedHosts"?: [ // Hostnames (or "*.example.com" wildcards) of the tools the visitor Keycloak token may be sent to (Optional)
        string
      ]
      "organizationLogin": object // How visitors sign in when the sign source is "organization" (mirrors the back-office login); null for other sign sources
    }
    "enableAnalysis"?: boolean // Optional
  }
  }
  "chatbot": object // Chatbot (Agent) summary from the conversation's inbox, already eager-loaded.

``is_maigpt`` lets the client tell the default MaiGPT agent apart (icon, feature
gate) without comparing against the organization's ``maigpt_inbox`` id;
``file_workspace_mode`` is the effective P6 mode (MaiGPT, blueprint and the
organization's file-library switch already applied).
  "title": string
  "lastMessage"?: { // Optional
  {
    "id": string (uuid)
    "type"?: string // Optional
    "content"?: string // Optional
    "createdAt": string (timestamp)
  }
  }
  "callRecord": 
  {
    "id": string (uuid)
    "status"?: string (enum: initiated, ringing, in-progress, completed, failed, busy, no-answer, canceled) // * `initiated` - Initiated
* `ringing` - Ringing
* `in-progress` - In Progress
* `completed` - Completed
* `failed` - Failed
* `busy` - Busy
* `no-answer` - No Answer
* `canceled` - Canceled (Optional)
    "direction": string (enum: inbound, outbound) // * `inbound` - Inbound
* `outbound` - Outbound
    "isActive": boolean
    "canHangup": boolean
  }
  "lastMessageCreatedAt": string (timestamp)
  "unreadMessagesCount": integer
  "autoReplyEnabled": boolean
  "isAutoReplyNow": boolean
  "willAiReplyNow": boolean // Whether a text message arriving through the inbox gets an AI reply right now.

The messaging channels are gated by ``is_auto_reply_now`` (auto reply on and inside the
inbox auto-reply schedule) and by the inbox having a chatbot. The chatbot's API web chat
is answered by the completions endpoint, which ignores the schedule, so there the
conversation switch alone decides. The channel is only looked up for a web inbox outside
its schedule.

Surfaces that ignore the schedule on any inbox answer from their own rule instead: the
completions endpoint continuing an existing conversation (``completions_progress_status``)
and voice turns, which the voice agent answers.
  "lastReadAt": string (timestamp)
  "createdAt": string (timestamp)
  "isGroupChat": boolean
  "enableGroupMention"?: boolean // Optional
  "queryMetadata"?: { // Query metadata controlling the scope of searchable knowledge; see each endpoint for details (Optional)
  {
    "knowledgeBases"?: [ // List of searchable knowledge bases; an empty array or null denies access to all knowledge bases (no permission); omission imposes no restriction (Optional)
      {
        "knowledgeBaseId": string (uuid) // Knowledge base ID
        "chatbotFileIds"?: [ // List of file IDs (excluded or exclusively selected according to hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "knowledgeBaseFileIds"?: [ // List of file IDs (the new name for chatbotFileIds; this field takes precedence when both are provided) (Optional)
          string (uuid)
        ]
        "faqIds"?: [ // List of FAQ IDs (excluded or exclusively selected according to hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "hasUserSelectedAll"?: boolean // true selects all knowledge base content except the listed items; false selects only the listed items (Optional)
      }
    ]
    "labelRelations"?: { // Tag filter conditions; an empty conditions array applies no tag filter (it does not deny access). Must be provided with knowledgeBases; when provided alone, no tag filter is applied to knowledge base retrieval (no restriction) (Optional)
    {
      "operator": string (enum: AND, OR) // How tag conditions are combined
      "conditions"?: [ // List of tag conditions; supports nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "toolMask"?: object // Enabled/disabled state per tool: {tool_id: bool} (Optional)
  "connectorMask"?: object // Enabled/disabled state per connector: {connector_id: bool} (Optional)
  "thinkingConfig"?: object // None = not set (inherit the chatbot setting); otherwise {"reasoning_effort": <level>} (Optional)
  "thinkingConfigSchema": { // Thinking configuration form for this model; null means the model has no thinking settings
  {
    "type"?: string // Form type, always reasoning_effort (Optional)
    "fields"?: object // reasoning_effort field: options lists the thinking levels accepted by this model (from lowest to highest); default is always null (Optional)
    "canDisable"?: boolean // Whether thinking can be disabled for this model; when true, options includes off (Optional)
    "unsetBehavior"?: string (enum: off, model_default) // Actual behavior when neither the conversation nor the assistant specifies a thinking level: off disables thinking (fastest); model_default lets the model decide or enables thinking at the platform default level (Optional)
    "unsetLevel"?: string // Level actually sent by the platform when none is set; null means no thinking parameter is sent, leaving the model default in effect (Optional)
  }
  }
  "effectiveThinkingConfig": object // The level the next reply sends on this conversation's model, after gating and clamping.

The stored choice resolves conversation > chatbot > the LLM row's default level; None means
the model sends no thinking parameter, so the provider default applies.
  "llm": string (uuid) // The model the next reply runs on: the conversation's pin, else its assistant's current model.

TC-0205: returning the stored ``conversation.llm`` would show a model an unpinned conversation no
longer uses once its assistant switches model, while ``thinking_config_schema`` already follows the
effective model. Read-only: displaying it never marks the conversation as explicitly selected;
only the update-llm endpoint does.
  "deepResearchStatus":  // Status of deep research for this conversation

* `not_used` - Not Used
* `started` - Started
* `running` - Running
* `completed` - Completed
* `failed` - Failed
  {
  }
  "deepResearchOutputFormat": // May have different types
  string (enum: canvas, chat, file) // Chosen delivery form for the finished report. Set only when the user clicks a format in the pre-research choice card; reset to NULL each time Deep Research is (re-)armed. NULL means no explicit choice (or a plan-skipping request) and falls back to Canvas at render time, but stays distinct from an explicit canvas pick so the frontend can lock the card only after a real selection. To roll back to fixed-Canvas behavior, force this to NULL/canvas — no redeploy needed.

* `canvas` - Canvas
* `chat` - Chat reply
* `file` - Downloadable file
  "ipAddress": string
  "ipCountryCode": string
  "ipRecordedAt": string (timestamp)
  "assignee": 
  {
    "id": string (uuid)
    "name": string
    "userId": string (uuid)
  }
  "status"?: string (enum: open, resolved, queued) // * `open` - Open
* `resolved` - Resolved
* `queued` - Queued (Optional)
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "memberIds"?: [ // Optional
        string (uuid)
      ]
    }
  ]
  "progressStatus": string
  "firstResponseAt"?: string (timestamp) // Optional
  "queuedAt"?: string (timestamp) // Optional
  "assignedAt"?: string (timestamp) // Optional
  "transferReason"?: string // Optional
  "satisfactionScore": integer // Conversation satisfaction rating on a 5-point scale (1=very dissatisfied, 5=very satisfied)
  "satisfactionComment": string
  "satisfactionSubmittedAt": string (timestamp)
  "latestAnalysisSummary": string // The conversation's current analysis summary (its active summary revision).

Reads the `latest_analysis_summary` annotation added by `setup_eager_loading`
to avoid an N+1 query. All read paths (list / retrieve / create) eager-load,
so the annotation is present; it falls back to '' only for instances built
outside `setup_eager_loading`.
  "clientPlatform":  // Client platform that created this conversation (web, desktop)

* `web` - Web
* `desktop` - Desktop
  {
  }
  "workingDirectory": string // Absolute path of the local folder bound to a desktop conversation. Only set for desktop conversations; null for web conversations.
  "slideProjectId": string
  "matchedField": string // Cached per instance: ``get_matched_message_id`` / ``get_matched_snippet``
both read this, so resolving it once avoids recomputing the keyword and
title-match logic three times per serialized conversation.
  "matchedSnippet": string // Keyword-centred excerpt of the matched message, with ellipses.

Reads the ``matched_message_content`` annotation added by
``ConversationViewSet._annotate_keyword_match``; no extra query.
  "matchedMessageId": string
  "pinnedAt": string (timestamp) // When the conversation was pinned. Null means unpinned.
  "folder": string (uuid) // The user-defined sidebar folder this conversation is grouped into. Null means unclassified (shown under the time groups). Deleting the folder resets this to null without deleting the conversation.
  "hasActiveBrowserSession": boolean // Whether a browser_use session snapshot exists to resume.

Lets the frontend know it should proactively re-request the browser
panel (emit ``browser:start``) after a page reload, instead of only
ever showing the panel when a live ``browser:frame`` push happens to
arrive first.
  "callerOrganizationId": string // Bound caller organization (stamped at conversation start); null when unbound
  "handoff": object // Handoff information: enabled is the platform toggle; the continuation conversation carries source_conversation_id/source_title; the original conversation being handed off carries continued_to_conversation_id/continued_to_title and latest_summary_message_id
  "displayContextUsage": boolean // Whether to show the context usage percentage bar (conversation platform toggle; always true for MaiGPT; independent of the handoff toggle)
  "autoSplitReason": string // Reason this conversation was created by automatic splitting: resolved (after resolution) / idle (inactivity); null if not created by splitting
  "continuedFrom": object // Conversation from which this split conversation continues: {id, last_message_at, deleted}; if the previous conversation was deleted, id=null and deleted=true
  "continuedTo": object // Most recent conversation split from this conversation: {id, created_at}; null if none
  "continuationSummary": object // Continuation summary: {state: disabled|pending|ready|not_applied, items: [{conversation_id, content, generated_at, last_message_at, old_status}]} (items ordered newest to oldest); null if not created by splitting
  "conversationKind": string // chat = a chat with an agent; secretary = the member's personal secretary Work chat
}
```

**Example Response Values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "contact": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "avatar": "Response string",
    "email": "response@example.com",
    "phoneNumber": "Response string",
    "metadata": null,
    "sourceId": "Response string",
    "tags": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string"
      }
    ]
  },
  "inbox": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "channelType": "line",
    "unreadConversationsCount": 456,
    "signAuth": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "signSource": "550e8400-e29b-41d4-a716-446655440000",
      "signParams": {
        "keycloak": {
          "clientId": "Response string",
          "url": "Response string",
          "realm": "Response string"
        },
        "line": {
          "liffId": "Response string"
        },
        "ad": {
          "saml": "Response string"
        },
        "google": {
          "enabled": false
        }
      },
      "sourceIdAccessEnabled": false,
      "sourceIdVerifyMode": {},
      "sourceIdSignatureTtlMinutes": {},
      "keycloakTokenPassthrough": false,
      "keycloakTokenPassthroughAllowedHosts": [
        "Response string"
      ],
      "organizationLogin": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      }
    },
    "enableAnalysis": false
  },
  "chatbot": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "title": "Response string",
  "lastMessage": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "Response string",
    "content": "Response string",
    "createdAt": "Response string"
  },
  "callRecord": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "initiated",
    "direction": "inbound",
    "isActive": false,
    "canHangup": false
  },
  "lastMessageCreatedAt": "Response string",
  "unreadMessagesCount": 456,
  "autoReplyEnabled": false,
  "isAutoReplyNow": false,
  "willAiReplyNow": false,
  "lastReadAt": "Response string",
  "createdAt": "Response string",
  "isGroupChat": false,
  "enableGroupMention": false,
  "queryMetadata": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "toolMask": null,
  "connectorMask": null,
  "thinkingConfig": null,
  "thinkingConfigSchema": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "effectiveThinkingConfig": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "llm": "550e8400-e29b-41d4-a716-446655440000",
  "deepResearchStatus": {},
  "deepResearchOutputFormat": "canvas",
  "ipAddress": "Response string",
  "ipCountryCode": "Response string",
  "ipRecordedAt": "Response string",
  "assignee": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "userId": "550e8400-e29b-41d4-a716-446655440000"
  },
  "status": "open",
  "tags": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string",
      "memberIds": [
        "550e8400-e29b-41d4-a716-446655440000"
      ]
    }
  ],
  "progressStatus": "Response string",
  "firstResponseAt": "Response string",
  "queuedAt": "Response string",
  "assignedAt": "Response string",
  "transferReason": "Response string",
  "satisfactionScore": 456,
  "satisfactionComment": "Response string",
  "satisfactionSubmittedAt": "Response string",
  "latestAnalysisSummary": "Response string",
  "clientPlatform": {},
  "workingDirectory": "Response string",
  "slideProjectId": "Response string",
  "matchedField": "Response string",
  "matchedSnippet": "Response string",
  "matchedMessageId": "Response string",
  "pinnedAt": "Response string",
  "folder": "550e8400-e29b-41d4-a716-446655440000",
  "hasActiveBrowserSession": false,
  "callerOrganizationId": "Response string",
  "handoff": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "displayContextUsage": false,
  "autoSplitReason": "Response string",
  "continuedFrom": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "continuedTo": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "continuationSummary": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "conversationKind": "Response string"
}
```

**Status code: 4XX - Error response; X-Error-Code carries its code (see "Error codes").**

***

### Claim Conversation <a href="#conversations-claim-partial-update" id="conversations-claim-partial-update"></a>

PATCH `/api/v1/conversations/{id}/claim/`

{% hint style="info" %}
Assigns the conversation to the caller, with the same effect as "Assign Conversation".
{% endhint %}

#### Parameters

| Parameter Name | Required | Type   | Description                                  |
| -------------- | -------- | ------ | -------------------------------------------- |
| `id`           | ✅        | string | A UUID string identifying this Conversation. |

#### Request Body

**Request Parameters**

| Field          | Type           | Required | Description |
| -------------- | -------------- | -------- | ----------- |
| transferReason | string         | No       |             |
| tagIds         | array\[string] | No       |             |

**Example Request Schema**

```typescript
{
  "transferReason"?: string // Optional
  "tagIds"?: [ // Optional
    string (uuid)
  ]
}
```

**Example Request Values**

```json
{
  "transferReason": "Example string",
  "tagIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ]
}
```

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X PATCH "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/claim/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "transferReason": "Example string",
    "tagIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ]
  }'

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
};

// Request body (payload)
const data = {
    "transferReason": "Example string",
    "tagIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ]
  };

axios.patch("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/claim/", data, config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request failed:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/claim/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request body (payload)
data = {
    "transferReason": "Example string",
    "tagIds": [
        "550e8400-e29b-41d4-a716-446655440000"
    ]
}

response = requests.patch(url, json=data, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request failed:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->patch("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/claim/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'transferReason' => 'Example string',
            'tagIds' => [
                '550e8400-e29b-41d4-a716-446655440000'
            ]
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request failed: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response body

**Status code: 200**

**Response schema example**

```typescript
{
  "id": string (uuid)
  "contact": { // Lightweight Contact Serializer for the Conversation List view

Includes only the fields required by the List view, omitting inboxes, mcp_credentials, and api_credentials
to avoid N+1 queries. email / phone_number / metadata are scalar fields on Contact itself,
needed for display in the frontend conversation sidebar, with no N+1 overhead.
  {
    "id": string (uuid)
    "name": string
    "avatar"?: string // Stores our own media as a relative storage key, or an external CDN URL as-is. (Optional)
    "email"?: // May have different types (Optional)
    string (email) // Optional
    "phoneNumber"?: string // Optional
    "metadata"?: object // Custom customer information shown in the customer panel of the conversation page (Optional)
    "sourceId"?: string // Optional
    "tags": [
      {
        "id": string (uuid)
        "name": string
      }
    ]
  }
  }
  "inbox": { // Inbox summary embedded in a conversation payload.

``unread_conversations_count`` stays here because it is part of the
documented REST conversation API. It is an org-wide denormalized
counter, so WS emit paths must run the payload through
:func:`strip_org_wide_unread_count` before broadcasting it to rooms
that include members without ``GroupInbox.can_view_conversations``.
  {
    "id": string (uuid)
    "name": string
    "channelType": string (enum: line, telegram, teams, web, messenger, instagram, email, whatsapp, slack, team_plus, line_works, viber) // * `line` - LINE
* `telegram` - Telegram
* `teams` - Teams
* `web` - Web
* `messenger` - Messenger
* `instagram` - Instagram
* `email` - Email
* `whatsapp` - WhatsApp
* `slack` - Slack
* `team_plus` - Team+
* `line_works` - LINE WORKS
* `viber` - Viber
    "unreadConversationsCount"?: integer // Optional
    "signAuth"?:  // Optional
    {
      "id": string (uuid)
      "signSource": string (uuid)
      "signParams": {
      {
        "keycloak": 
        {
          "clientId": string
          "url": string
          "realm": string
        }
        "line": 
        {
          "liffId": string
        }
        "ad": 
        {
          "saml": string
        }
        "google"?:  // Optional
        {
          "enabled": boolean
        }
      }
      }
      "sourceIdAccessEnabled"?: boolean // When enabled, embedded visitors presenting a Source ID (verified per the verify mode below) may chat without going through the login flow. Disabled keeps every existing behavior unchanged. (Optional)
      "sourceIdVerifyMode"?:  // "Require signature verification": the embedding site must send an HMAC signature computed on its backend. "Source ID only": anyone knowing a Source ID can act as that contact; suitable only for testing or closed networks.

* `signature` - Require signature verification
* `raw` - Source ID only (Optional)
      {
      }
      "sourceIdSignatureTtlMinutes"?:  // How long a signature can be used to obtain the contact identity. Not a session lifetime: once exchanged, the session follows the existing WebChat mechanism.

* `1` - 1
* `5` - 5
* `15` - 15 (Optional)
      {
      }
      "keycloakTokenPassthrough"?: boolean // When enabled, the Keycloak access token a visitor logged in with is forwarded as the Authorization header when the AI calls MCP and API tools on their behalf, so tools do not require a separate per-tool login. Only for the Keycloak sign source. Never overrides an Authorization header a tool already resolves from its own credentials. The visitor identity is the organization member bound to the conversation at login, never the contact Source ID. (Optional)
      "keycloakTokenPassthroughAllowedHosts"?: [ // Hostnames (or "*.example.com" wildcards) of the tools the visitor Keycloak token may be sent to (Optional)
        string
      ]
      "organizationLogin": object // How visitors sign in when the sign source is "organization" (mirrors the back-office login); null for other sign sources
    }
    "enableAnalysis"?: boolean // Optional
  }
  }
  "chatbot": object // Chatbot (Agent) summary from the conversation's inbox, already eager-loaded.

``is_maigpt`` lets the client tell the default MaiGPT agent apart (icon, feature
gate) without comparing against the organization's ``maigpt_inbox`` id;
``file_workspace_mode`` is the effective P6 mode (MaiGPT, blueprint and the
organization's file-library switch already applied).
  "title": string
  "lastMessage"?: { // Optional
  {
    "id": string (uuid)
    "type"?: string // Optional
    "content"?: string // Optional
    "createdAt": string (timestamp)
  }
  }
  "callRecord": 
  {
    "id": string (uuid)
    "status"?: string (enum: initiated, ringing, in-progress, completed, failed, busy, no-answer, canceled) // * `initiated` - Initiated
* `ringing` - Ringing
* `in-progress` - In Progress
* `completed` - Completed
* `failed` - Failed
* `busy` - Busy
* `no-answer` - No Answer
* `canceled` - Canceled (Optional)
    "direction": string (enum: inbound, outbound) // * `inbound` - Inbound
* `outbound` - Outbound
    "isActive": boolean
    "canHangup": boolean
  }
  "lastMessageCreatedAt": string (timestamp)
  "unreadMessagesCount": integer
  "autoReplyEnabled": boolean
  "isAutoReplyNow": boolean
  "willAiReplyNow": boolean // Whether a text message arriving through the inbox gets an AI reply right now.

The messaging channels are gated by ``is_auto_reply_now`` (auto reply on and inside the
inbox auto-reply schedule) and by the inbox having a chatbot. The chatbot's API web chat
is answered by the completions endpoint, which ignores the schedule, so there the
conversation switch alone decides. The channel is only looked up for a web inbox outside
its schedule.

Surfaces that ignore the schedule on any inbox answer from their own rule instead: the
completions endpoint continuing an existing conversation (``completions_progress_status``)
and voice turns, which the voice agent answers.
  "lastReadAt": string (timestamp)
  "createdAt": string (timestamp)
  "isGroupChat": boolean
  "enableGroupMention"?: boolean // Optional
  "queryMetadata"?: { // Query metadata controlling the scope of searchable knowledge; see the documentation for each endpoint (Optional)
  {
    "knowledgeBases"?: [ // List of searchable knowledge bases; an empty array or null denies access to all knowledge bases, while omission imposes no restriction (Optional)
      {
        "knowledgeBaseId": string (uuid) // Knowledge base ID
        "chatbotFileIds"?: [ // List of file IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "knowledgeBaseFileIds"?: [ // List of file IDs (the new name for chatbotFileIds; this field takes precedence when both are provided) (Optional)
          string (uuid)
        ]
        "faqIds"?: [ // List of FAQ IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "hasUserSelectedAll"?: boolean // true selects all knowledge base content except the listed items; false selects only the listed items (Optional)
      }
    ]
    "labelRelations"?: { // Tag filter criteria; an empty conditions array disables tag filtering (it does not deny access). Must be provided with knowledgeBases; when provided alone, tag filtering is not applied to knowledge base retrieval (no restriction) (Optional)
    {
      "operator": string (enum: AND, OR) // How tag conditions are combined
      "conditions"?: [ // List of tag conditions supporting nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "toolMask"?: object // Enabled/disabled state per tool: {tool_id: bool} (Optional)
  "connectorMask"?: object // Enabled/disabled state per connector: {connector_id: bool} (Optional)
  "thinkingConfig"?: object // None = not set (inherit the chatbot setting); otherwise {"reasoning_effort": <level>} (Optional)
  "thinkingConfigSchema": { // Thinking configuration form for this model; null means the model has no thinking settings
  {
    "type"?: string // Form type, always reasoning_effort (Optional)
    "fields"?: object // reasoning_effort field: options lists the thinking levels accepted by this model (from lowest to highest); default is always null (Optional)
    "canDisable"?: boolean // Whether thinking can be disabled for this model; if true, options includes off (Optional)
    "unsetBehavior"?: string (enum: off, model_default) // Actual behavior when neither the conversation nor the assistant has a thinking level set: off disables thinking (fastest); model_default lets the model decide or enables thinking through the platform default level (Optional)
    "unsetLevel"?: string // Level actually sent by the platform when no level is set; null omits the thinking parameter and uses the model default (Optional)
  }
  }
  "effectiveThinkingConfig": object // The level the next reply sends on this conversation's model, after gating and clamping.

The stored choice resolves conversation > chatbot > the LLM row's default level; None means
the model sends no thinking parameter, so the provider default applies.
  "llm": string (uuid) // The model the next reply runs on: the conversation's pin, else its assistant's current model.

TC-0205: returning the stored ``conversation.llm`` would show a model an unpinned conversation no
longer uses once its assistant switches model, while ``thinking_config_schema`` already follows the
effective model. Read-only: displaying it never marks the conversation as explicitly selected;
only the update-llm endpoint does.
  "deepResearchStatus":  // Status of deep research for this conversation

* `not_used` - Not Used
* `started` - Started
* `running` - Running
* `completed` - Completed
* `failed` - Failed
  {
  }
  "deepResearchOutputFormat": // May have different types
  string (enum: canvas, chat, file) // Chosen delivery form for the finished report. Set only when the user clicks a format in the pre-research choice card; reset to NULL each time Deep Research is (re-)armed. NULL means no explicit choice (or a plan-skipping request) and falls back to Canvas at render time, but stays distinct from an explicit canvas pick so the frontend can lock the card only after a real selection. To roll back to fixed-Canvas behavior, force this to NULL/canvas — no redeploy needed.

* `canvas` - Canvas
* `chat` - Chat reply
* `file` - Downloadable file
  "ipAddress": string
  "ipCountryCode": string
  "ipRecordedAt": string (timestamp)
  "assignee": 
  {
    "id": string (uuid)
    "name": string
    "userId": string (uuid)
  }
  "status"?: string (enum: open, resolved, queued) // * `open` - Open
* `resolved` - Resolved
* `queued` - Queued (Optional)
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "memberIds"?: [ // Optional
        string (uuid)
      ]
    }
  ]
  "progressStatus": string
  "firstResponseAt"?: string (timestamp) // Optional
  "queuedAt"?: string (timestamp) // Optional
  "assignedAt"?: string (timestamp) // Optional
  "transferReason"?: string // Optional
  "satisfactionScore": integer // Conversation satisfaction rating on a 5-point scale (1=very dissatisfied, 5=very satisfied)
  "satisfactionComment": string
  "satisfactionSubmittedAt": string (timestamp)
  "latestAnalysisSummary": string // The conversation's current analysis summary (its active summary revision).

Reads the `latest_analysis_summary` annotation added by `setup_eager_loading`
to avoid an N+1 query. All read paths (list / retrieve / create) eager-load,
so the annotation is present; it falls back to '' only for instances built
outside `setup_eager_loading`.
  "clientPlatform":  // Client platform that created this conversation (web, desktop)

* `web` - Web
* `desktop` - Desktop
  {
  }
  "workingDirectory": string // Absolute path of the local folder bound to a desktop conversation. Only set for desktop conversations; null for web conversations.
  "slideProjectId": string
  "matchedField": string // Cached per instance: ``get_matched_message_id`` / ``get_matched_snippet``
both read this, so resolving it once avoids recomputing the keyword and
title-match logic three times per serialized conversation.
  "matchedSnippet": string // Keyword-centred excerpt of the matched message, with ellipses.

Reads the ``matched_message_content`` annotation added by
``ConversationViewSet._annotate_keyword_match``; no extra query.
  "matchedMessageId": string
  "pinnedAt": string (timestamp) // When the conversation was pinned. Null means unpinned.
  "folder": string (uuid) // The user-defined sidebar folder this conversation is grouped into. Null means unclassified (shown under the time groups). Deleting the folder resets this to null without deleting the conversation.
  "hasActiveBrowserSession": boolean // Whether a browser_use session snapshot exists to resume.

Lets the frontend know it should proactively re-request the browser
panel (emit ``browser:start``) after a page reload, instead of only
ever showing the panel when a live ``browser:frame`` push happens to
arrive first.
  "callerOrganizationId": string // Bound caller organization (stamped at conversation start); null when unbound
  "handoff": object // Handoff information: enabled is the platform toggle; the continuing conversation includes source_conversation_id/source_title; the original conversation being handed off includes continued_to_conversation_id/continued_to_title and latest_summary_message_id
  "displayContextUsage": boolean // Whether to display the context usage percentage bar (conversation platform toggle, always true for MaiGPT; independent of the handoff toggle)
  "autoSplitReason": string // Reason this conversation was created by automatic splitting: resolved (after resolution) / idle (inactivity); null if not created by splitting
  "continuedFrom": object // Conversation from which this split conversation continues: {id, last_message_at, deleted}; if the previous conversation was deleted, id=null and deleted=true
  "continuedTo": object // Most recent conversation split from this conversation: {id, created_at}; null if none
  "continuationSummary": object // Continuation summary: {state: disabled|pending|ready|not_applied, items: [{conversation_id, content, generated_at, last_message_at, old_status}]} (items ordered newest to oldest); null if not created by splitting
  "conversationKind": string // chat = a chat with an agent; secretary = the member's personal secretary Work chat
}
```

**Example response values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "contact": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "avatar": "Response string",
    "email": "response@example.com",
    "phoneNumber": "Response string",
    "metadata": null,
    "sourceId": "Response string",
    "tags": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string"
      }
    ]
  },
  "inbox": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "channelType": "line",
    "unreadConversationsCount": 456,
    "signAuth": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "signSource": "550e8400-e29b-41d4-a716-446655440000",
      "signParams": {
        "keycloak": {
          "clientId": "Response string",
          "url": "Response string",
          "realm": "Response string"
        },
        "line": {
          "liffId": "Response string"
        },
        "ad": {
          "saml": "Response string"
        },
        "google": {
          "enabled": false
        }
      },
      "sourceIdAccessEnabled": false,
      "sourceIdVerifyMode": {},
      "sourceIdSignatureTtlMinutes": {},
      "keycloakTokenPassthrough": false,
      "keycloakTokenPassthroughAllowedHosts": [
        "Response string"
      ],
      "organizationLogin": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      }
    },
    "enableAnalysis": false
  },
  "chatbot": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "title": "Response string",
  "lastMessage": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "Response string",
    "content": "Response string",
    "createdAt": "Response string"
  },
  "callRecord": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "initiated",
    "direction": "inbound",
    "isActive": false,
    "canHangup": false
  },
  "lastMessageCreatedAt": "Response string",
  "unreadMessagesCount": 456,
  "autoReplyEnabled": false,
  "isAutoReplyNow": false,
  "willAiReplyNow": false,
  "lastReadAt": "Response string",
  "createdAt": "Response string",
  "isGroupChat": false,
  "enableGroupMention": false,
  "queryMetadata": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "toolMask": null,
  "connectorMask": null,
  "thinkingConfig": null,
  "thinkingConfigSchema": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "effectiveThinkingConfig": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "llm": "550e8400-e29b-41d4-a716-446655440000",
  "deepResearchStatus": {},
  "deepResearchOutputFormat": "canvas",
  "ipAddress": "Response string",
  "ipCountryCode": "Response string",
  "ipRecordedAt": "Response string",
  "assignee": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "userId": "550e8400-e29b-41d4-a716-446655440000"
  },
  "status": "open",
  "tags": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string",
      "memberIds": [
        "550e8400-e29b-41d4-a716-446655440000"
      ]
    }
  ],
  "progressStatus": "Response string",
  "firstResponseAt": "Response string",
  "queuedAt": "Response string",
  "assignedAt": "Response string",
  "transferReason": "Response string",
  "satisfactionScore": 456,
  "satisfactionComment": "Response string",
  "satisfactionSubmittedAt": "Response string",
  "latestAnalysisSummary": "Response string",
  "clientPlatform": {},
  "workingDirectory": "Response string",
  "slideProjectId": "Response string",
  "matchedField": "Response string",
  "matchedSnippet": "Response string",
  "matchedMessageId": "Response string",
  "pinnedAt": "Response string",
  "folder": "550e8400-e29b-41d4-a716-446655440000",
  "hasActiveBrowserSession": false,
  "callerOrganizationId": "Response string",
  "handoff": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "displayContextUsage": false,
  "autoSplitReason": "Response string",
  "continuedFrom": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "continuedTo": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "continuationSummary": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "conversationKind": "Response string"
}
```

**Status code: 4XX - Error response; X-Error-Code carries its code (see "Error codes").**

***

### Transfer a conversation to another member <a href="#conversations-transfer-partial-update" id="conversations-transfer-partial-update"></a>

PATCH `/api/v1/conversations/{id}/transfer/`

#### Parameters

| Parameter name | Required | Type   | Description                                  |
| -------------- | -------- | ------ | -------------------------------------------- |
| `id`           | ✅        | string | A UUID string identifying this Conversation. |

#### Request body

**Request parameters**

| Field          | Type           | Required | Description |
| -------------- | -------------- | -------- | ----------- |
| assignee       | string (uuid)  | No       |             |
| transferReason | string         | No       |             |
| tagIds         | array\[string] | No       |             |

**Request schema example**

```typescript
{
  "assignee"?: string (uuid) // Optional
  "transferReason"?: string // Optional
  "tagIds"?: [ // Optional
    string (uuid)
  ]
}
```

**Example request values**

```json
{
  "assignee": "550e8400-e29b-41d4-a716-446655440000",
  "transferReason": "Example string",
  "tagIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ]
}
```

#### Code examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X PATCH "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/transfer/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "assignee": "550e8400-e29b-41d4-a716-446655440000",
    "transferReason": "Example string",
    "tagIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ]
  }'

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Configure request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
};

// Request body (payload)
const data = {
    "assignee": "550e8400-e29b-41d4-a716-446655440000",
    "transferReason": "Example string",
    "tagIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ]
  };

axios.patch("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/transfer/", data, config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request failed:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/transfer/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request body (payload)
data = {
    "assignee": "550e8400-e29b-41d4-a716-446655440000",
    "transferReason": "Example string",
    "tagIds": [
        "550e8400-e29b-41d4-a716-446655440000"
    ]
}

response = requests.patch(url, json=data, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request failed:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->patch("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/transfer/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'assignee' => '550e8400-e29b-41d4-a716-446655440000',
            'transferReason' => 'Example string',
            'tagIds' => [
                '550e8400-e29b-41d4-a716-446655440000'
            ]
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request failed: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 200**

**Response Schema Example**

```typescript
{
  "id": string (uuid)
  "contact": { // Lightweight Contact Serializer for the Conversation List view

Includes only the fields required for the List view, omitting inboxes, mcp_credentials, and api_credentials
to avoid N+1 queries. email / phone_number / metadata are scalar fields on Contact itself;
the frontend conversation sidebar needs to display them, with no N+1 cost.
  {
    "id": string (uuid)
    "name": string
    "avatar"?: string // Stores our own media as a relative storage key, or an external CDN URL as-is. (Optional)
    "email"?: // May have different types (Optional)
    string (email) // Optional
    "phoneNumber"?: string // Optional
    "metadata"?: object // Custom customer information shown in the customer panel of the conversation page (Optional)
    "sourceId"?: string // Optional
    "tags": [
      {
        "id": string (uuid)
        "name": string
      }
    ]
  }
  }
  "inbox": { // Inbox summary embedded in a conversation payload.

``unread_conversations_count`` stays here because it is part of the
documented REST conversation API. It is an org-wide denormalized
counter, so WS emit paths must run the payload through
:func:`strip_org_wide_unread_count` before broadcasting it to rooms
that include members without ``GroupInbox.can_view_conversations``.
  {
    "id": string (uuid)
    "name": string
    "channelType": string (enum: line, telegram, teams, web, messenger, instagram, email, whatsapp, slack, team_plus, line_works, viber) // * `line` - LINE
* `telegram` - Telegram
* `teams` - Teams
* `web` - Web
* `messenger` - Messenger
* `instagram` - Instagram
* `email` - Email
* `whatsapp` - WhatsApp
* `slack` - Slack
* `team_plus` - Team+
* `line_works` - LINE WORKS
* `viber` - Viber
    "unreadConversationsCount"?: integer // Optional
    "signAuth"?:  // Optional
    {
      "id": string (uuid)
      "signSource": string (uuid)
      "signParams": {
      {
        "keycloak": 
        {
          "clientId": string
          "url": string
          "realm": string
        }
        "line": 
        {
          "liffId": string
        }
        "ad": 
        {
          "saml": string
        }
        "google"?:  // Optional
        {
          "enabled": boolean
        }
      }
      }
      "sourceIdAccessEnabled"?: boolean // When enabled, embedded visitors presenting a Source ID (verified per the verify mode below) may chat without going through the login flow. Disabled keeps every existing behavior unchanged. (Optional)
      "sourceIdVerifyMode"?:  // "Require signature verification": the embedding site must send an HMAC signature computed on its backend. "Source ID only": anyone knowing a Source ID can act as that contact; suitable only for testing or closed networks.

* `signature` - Require signature verification
* `raw` - Source ID only (Optional)
      {
      }
      "sourceIdSignatureTtlMinutes"?:  // How long a signature can be used to obtain the contact identity. Not a session lifetime: once exchanged, the session follows the existing WebChat mechanism.

* `1` - 1
* `5` - 5
* `15` - 15 (Optional)
      {
      }
      "keycloakTokenPassthrough"?: boolean // When enabled, the Keycloak access token a visitor logged in with is forwarded as the Authorization header when the AI calls MCP and API tools on their behalf, so tools do not require a separate per-tool login. Only for the Keycloak sign source. Never overrides an Authorization header a tool already resolves from its own credentials. The visitor identity is the organization member bound to the conversation at login, never the contact Source ID. (Optional)
      "keycloakTokenPassthroughAllowedHosts"?: [ // Hostnames (or "*.example.com" wildcards) of the tools the visitor Keycloak token may be sent to (Optional)
        string
      ]
      "organizationLogin": object // How visitors sign in when the sign source is "organization" (mirrors the back-office login); null for other sign sources
    }
    "enableAnalysis"?: boolean // Optional
  }
  }
  "chatbot": object // Chatbot (Agent) summary from the conversation's inbox, already eager-loaded.

``is_maigpt`` lets the client tell the default MaiGPT agent apart (icon, feature
gate) without comparing against the organization's ``maigpt_inbox`` id;
``file_workspace_mode`` is the effective P6 mode (MaiGPT, blueprint and the
organization's file-library switch already applied).
  "title": string
  "lastMessage"?: { // Optional
  {
    "id": string (uuid)
    "type"?: string // Optional
    "content"?: string // Optional
    "createdAt": string (timestamp)
  }
  }
  "callRecord": 
  {
    "id": string (uuid)
    "status"?: string (enum: initiated, ringing, in-progress, completed, failed, busy, no-answer, canceled) // * `initiated` - Initiated
* `ringing` - Ringing
* `in-progress` - In Progress
* `completed` - Completed
* `failed` - Failed
* `busy` - Busy
* `no-answer` - No Answer
* `canceled` - Canceled (Optional)
    "direction": string (enum: inbound, outbound) // * `inbound` - Inbound
* `outbound` - Outbound
    "isActive": boolean
    "canHangup": boolean
  }
  "lastMessageCreatedAt": string (timestamp)
  "unreadMessagesCount": integer
  "autoReplyEnabled": boolean
  "isAutoReplyNow": boolean
  "willAiReplyNow": boolean // Whether a text message arriving through the inbox gets an AI reply right now.

The messaging channels are gated by ``is_auto_reply_now`` (auto reply on and inside the
inbox auto-reply schedule) and by the inbox having a chatbot. The chatbot's API web chat
is answered by the completions endpoint, which ignores the schedule, so there the
conversation switch alone decides. The channel is only looked up for a web inbox outside
its schedule.

Surfaces that ignore the schedule on any inbox answer from their own rule instead: the
completions endpoint continuing an existing conversation (``completions_progress_status``)
and voice turns, which the voice agent answers.
  "lastReadAt": string (timestamp)
  "createdAt": string (timestamp)
  "isGroupChat": boolean
  "enableGroupMention"?: boolean // Optional
  "queryMetadata"?: { // Query metadata controlling which knowledge can be queried; see each endpoint for details (Optional)
  {
    "knowledgeBases"?: [ // List of queryable knowledge bases; an empty array or null denies all access (no permission), while omitting this field imposes no restriction (Optional)
      {
        "knowledgeBaseId": string (uuid) // Knowledge base ID
        "chatbotFileIds"?: [ // List of file IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "knowledgeBaseFileIds"?: [ // List of file IDs (the new name for chatbotFileIds; this field takes precedence when both are provided) (Optional)
          string (uuid)
        ]
        "faqIds"?: [ // List of FAQ IDs (excluded or exclusively selected depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "hasUserSelectedAll"?: boolean // true selects all knowledge base content except the listed items; false selects only the listed items (Optional)
      }
    ]
    "labelRelations"?: { // Label filter criteria; an empty conditions array disables label filtering (it does not deny access). Must be provided with knowledgeBases; when provided alone, knowledge base retrieval does not apply label filtering (no restriction) (Optional)
    {
      "operator": string (enum: AND, OR) // How label conditions are combined
      "conditions"?: [ // List of label conditions, supporting nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "toolMask"?: object // Enabled/disabled state per tool: {tool_id: bool} (Optional)
  "connectorMask"?: object // Enabled/disabled state per connector: {connector_id: bool} (Optional)
  "thinkingConfig"?: object // None = not set (inherit the chatbot setting); otherwise {"reasoning_effort": <level>} (Optional)
  "thinkingConfigSchema": { // Thinking configuration form for this model; null means the model has no thinking configuration
  {
    "type"?: string // Form type, always reasoning_effort (Optional)
    "fields"?: object // reasoning_effort field: options lists the thinking levels accepted by this model (from lowest to highest); default is always null (Optional)
    "canDisable"?: boolean // Whether thinking can be disabled for this model; when true, options includes off (Optional)
    "unsetBehavior"?: string (enum: off, model_default) // Actual behavior when neither the conversation nor the assistant has a thinking level set: off disables thinking (fastest); model_default lets the model decide or enables thinking through the platform default level (Optional)
    "unsetLevel"?: string // Level actually sent by the platform when no level is set; null omits the thinking parameter and uses the model default (Optional)
  }
  }
  "effectiveThinkingConfig": object // The level the next reply sends on this conversation's model, after gating and clamping.

The stored choice resolves conversation > chatbot > the LLM row's default level; None means
the model sends no thinking parameter, so the provider default applies.
  "llm": string (uuid) // The model the next reply runs on: the conversation's pin, else its assistant's current model.

TC-0205: returning the stored ``conversation.llm`` would show a model an unpinned conversation no
longer uses once its assistant switches model, while ``thinking_config_schema`` already follows the
effective model. Read-only: displaying it never marks the conversation as explicitly selected;
only the update-llm endpoint does.
  "deepResearchStatus":  // Status of deep research for this conversation

* `not_used` - Not Used
* `started` - Started
* `running` - Running
* `completed` - Completed
* `failed` - Failed
  {
  }
  "deepResearchOutputFormat": // May have different types
  string (enum: canvas, chat, file) // Chosen delivery form for the finished report. Set only when the user clicks a format in the pre-research choice card; reset to NULL each time Deep Research is (re-)armed. NULL means no explicit choice (or a plan-skipping request) and falls back to Canvas at render time, but stays distinct from an explicit canvas pick so the frontend can lock the card only after a real selection. To roll back to fixed-Canvas behavior, force this to NULL/canvas — no redeploy needed.

* `canvas` - Canvas
* `chat` - Chat reply
* `file` - Downloadable file
  "ipAddress": string
  "ipCountryCode": string
  "ipRecordedAt": string (timestamp)
  "assignee": 
  {
    "id": string (uuid)
    "name": string
    "userId": string (uuid)
  }
  "status"?: string (enum: open, resolved, queued) // * `open` - Open
* `resolved` - Resolved
* `queued` - Queued (Optional)
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "memberIds"?: [ // Optional
        string (uuid)
      ]
    }
  ]
  "progressStatus": string
  "firstResponseAt"?: string (timestamp) // Optional
  "queuedAt"?: string (timestamp) // Optional
  "assignedAt"?: string (timestamp) // Optional
  "transferReason"?: string // Optional
  "satisfactionScore": integer // Conversation satisfaction rating on a 5-point scale (1=very dissatisfied, 5=very satisfied)
  "satisfactionComment": string
  "satisfactionSubmittedAt": string (timestamp)
  "latestAnalysisSummary": string // The conversation's current analysis summary (its active summary revision).

Reads the `latest_analysis_summary` annotation added by `setup_eager_loading`
to avoid an N+1 query. All read paths (list / retrieve / create) eager-load,
so the annotation is present; it falls back to '' only for instances built
outside `setup_eager_loading`.
  "clientPlatform":  // Client platform that created this conversation (web, desktop)

* `web` - Web
* `desktop` - Desktop
  {
  }
  "workingDirectory": string // Absolute path of the local folder bound to a desktop conversation. Only set for desktop conversations; null for web conversations.
  "slideProjectId": string
  "matchedField": string // Cached per instance: ``get_matched_message_id`` / ``get_matched_snippet``
both read this, so resolving it once avoids recomputing the keyword and
title-match logic three times per serialized conversation.
  "matchedSnippet": string // Keyword-centred excerpt of the matched message, with ellipses.

Reads the ``matched_message_content`` annotation added by
``ConversationViewSet._annotate_keyword_match``; no extra query.
  "matchedMessageId": string
  "pinnedAt": string (timestamp) // When the conversation was pinned. Null means unpinned.
  "folder": string (uuid) // The user-defined sidebar folder this conversation is grouped into. Null means unclassified (shown under the time groups). Deleting the folder resets this to null without deleting the conversation.
  "hasActiveBrowserSession": boolean // Whether a browser_use session snapshot exists to resume.

Lets the frontend know it should proactively re-request the browser
panel (emit ``browser:start``) after a page reload, instead of only
ever showing the panel when a live ``browser:frame`` push happens to
arrive first.
  "callerOrganizationId": string // Bound caller organization (stamped at conversation start); null when unbound
  "handoff": object // Handoff information: enabled is the platform switch; the continuing conversation includes source_conversation_id/source_title; the original conversation being handed off includes continued_to_conversation_id/continued_to_title and latest_summary_message_id
  "displayContextUsage": boolean // Whether to show the context usage percentage bar (a conversation platform switch, always true for MaiGPT; independent of the handoff switch)
  "autoSplitReason": string // Reason this conversation was created by automatic splitting: resolved (after resolution) or idle; null if not created by splitting
  "continuedFrom": object // Conversation from which this split conversation continues: {id, last_message_at, deleted}; if the old conversation has been deleted, id=null and deleted=true
  "continuedTo": object // Latest conversation split from this conversation: {id, created_at}; null if none
  "continuationSummary": object // Continuation summary: {state: disabled|pending|ready|not_applied, items: [{conversation_id, content, generated_at, last_message_at, old_status}]} (items ordered newest to oldest); null if not created by splitting
  "conversationKind": string // chat = a chat with an agent; secretary = the member's personal secretary Work chat
}
```

**Example Response Values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "contact": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "avatar": "Response string",
    "email": "response@example.com",
    "phoneNumber": "Response string",
    "metadata": null,
    "sourceId": "Response string",
    "tags": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string"
      }
    ]
  },
  "inbox": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "channelType": "line",
    "unreadConversationsCount": 456,
    "signAuth": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "signSource": "550e8400-e29b-41d4-a716-446655440000",
      "signParams": {
        "keycloak": {
          "clientId": "Response string",
          "url": "Response string",
          "realm": "Response string"
        },
        "line": {
          "liffId": "Response string"
        },
        "ad": {
          "saml": "Response string"
        },
        "google": {
          "enabled": false
        }
      },
      "sourceIdAccessEnabled": false,
      "sourceIdVerifyMode": {},
      "sourceIdSignatureTtlMinutes": {},
      "keycloakTokenPassthrough": false,
      "keycloakTokenPassthroughAllowedHosts": [
        "Response string"
      ],
      "organizationLogin": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      }
    },
    "enableAnalysis": false
  },
  "chatbot": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "title": "Response string",
  "lastMessage": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "Response string",
    "content": "Response string",
    "createdAt": "Response string"
  },
  "callRecord": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "initiated",
    "direction": "inbound",
    "isActive": false,
    "canHangup": false
  },
  "lastMessageCreatedAt": "Response string",
  "unreadMessagesCount": 456,
  "autoReplyEnabled": false,
  "isAutoReplyNow": false,
  "willAiReplyNow": false,
  "lastReadAt": "Response string",
  "createdAt": "Response string",
  "isGroupChat": false,
  "enableGroupMention": false,
  "queryMetadata": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "toolMask": null,
  "connectorMask": null,
  "thinkingConfig": null,
  "thinkingConfigSchema": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "effectiveThinkingConfig": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "llm": "550e8400-e29b-41d4-a716-446655440000",
  "deepResearchStatus": {},
  "deepResearchOutputFormat": "canvas",
  "ipAddress": "Response string",
  "ipCountryCode": "Response string",
  "ipRecordedAt": "Response string",
  "assignee": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "userId": "550e8400-e29b-41d4-a716-446655440000"
  },
  "status": "open",
  "tags": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string",
      "memberIds": [
        "550e8400-e29b-41d4-a716-446655440000"
      ]
    }
  ],
  "progressStatus": "Response string",
  "firstResponseAt": "Response string",
  "queuedAt": "Response string",
  "assignedAt": "Response string",
  "transferReason": "Response string",
  "satisfactionScore": 456,
  "satisfactionComment": "Response string",
  "satisfactionSubmittedAt": "Response string",
  "latestAnalysisSummary": "Response string",
  "clientPlatform": {},
  "workingDirectory": "Response string",
  "slideProjectId": "Response string",
  "matchedField": "Response string",
  "matchedSnippet": "Response string",
  "matchedMessageId": "Response string",
  "pinnedAt": "Response string",
  "folder": "550e8400-e29b-41d4-a716-446655440000",
  "hasActiveBrowserSession": false,
  "callerOrganizationId": "Response string",
  "handoff": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "displayContextUsage": false,
  "autoSplitReason": "Response string",
  "continuedFrom": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "continuedTo": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "continuationSummary": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "conversationKind": "Response string"
}
```

**Status Code: 4XX - Error response; X-Error-Code carries its code (see "Error codes").**

***

### Return to AI Replies <a href="#conversations-return-to-ai-partial-update" id="conversations-return-to-ai-partial-update"></a>

PATCH `/api/v1/conversations/{id}/return-to-ai/`

{% hint style="info" %}
Clear the assigned member and resume automatic AI replies. Sends a `handoff.returned_to_ai` Webhook event on completion.
{% endhint %}

#### Parameters

| Parameter Name | Required | Type   | Description                                  |
| -------------- | -------- | ------ | -------------------------------------------- |
| `id`           | ✅        | string | A UUID string identifying this Conversation. |

#### Request Body

**Request Parameters**

| Field               | Type    | Required | Description |
| ------------------- | ------- | -------- | ----------- |
| requireContextReady | boolean | No       |             |

**Request Schema Example**

```typescript
{
  "requireContextReady"?: boolean // Optional
}
```

**Example Request Values**

```json
{
  "requireContextReady": true
}
```

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X PATCH "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/return-to-ai/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "requireContextReady": true
  }'

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Configure request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
};

// Request body (payload)
const data = {
    "requireContextReady": true
  };

axios.patch("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/return-to-ai/", data, config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request failed:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/return-to-ai/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request Body (payload)
data = {
    "requireContextReady": True
}

response = requests.patch(url, json=data, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request failed:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->patch("https://api.maiagent.ai/api/v1/conversations/550e8400-e29b-41d4-a716-446655440000/return-to-ai/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'requireContextReady' => true
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request failed: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 200**

**Example Response Schema**

```typescript
{
  "id": string (uuid)
  "contact": { // Lightweight Contact Serializer for the Conversation List view

Includes only the fields required by the List view, omitting inboxes, mcp_credentials, and api_credentials
to avoid N+1 queries. email / phone_number / metadata are scalar fields on Contact itself,
displayed in the frontend conversation sidebar with no N+1 overhead.
  {
    "id": string (uuid)
    "name": string
    "avatar"?: string // Stores our own media as a relative storage key, or an external CDN URL as-is. (Optional)
    "email"?: // May have different types (Optional)
    string (email) // Optional
    "phoneNumber"?: string // Optional
    "metadata"?: object // Custom customer information shown in the customer panel of the conversation page (Optional)
    "sourceId"?: string // Optional
    "tags": [
      {
        "id": string (uuid)
        "name": string
      }
    ]
  }
  }
  "inbox": { // Inbox summary embedded in a conversation payload.

``unread_conversations_count`` stays here because it is part of the
documented REST conversation API. It is an org-wide denormalized
counter, so WS emit paths must run the payload through
:func:`strip_org_wide_unread_count` before broadcasting it to rooms
that include members without ``GroupInbox.can_view_conversations``.
  {
    "id": string (uuid)
    "name": string
    "channelType": string (enum: line, telegram, teams, web, messenger, instagram, email, whatsapp, slack, team_plus, line_works, viber) // * `line` - LINE
* `telegram` - Telegram
* `teams` - Teams
* `web` - Web
* `messenger` - Messenger
* `instagram` - Instagram
* `email` - Email
* `whatsapp` - WhatsApp
* `slack` - Slack
* `team_plus` - Team+
* `line_works` - LINE WORKS
* `viber` - Viber
    "unreadConversationsCount"?: integer // Optional
    "signAuth"?:  // Optional
    {
      "id": string (uuid)
      "signSource": string (uuid)
      "signParams": {
      {
        "keycloak": 
        {
          "clientId": string
          "url": string
          "realm": string
        }
        "line": 
        {
          "liffId": string
        }
        "ad": 
        {
          "saml": string
        }
        "google"?:  // Optional
        {
          "enabled": boolean
        }
      }
      }
      "sourceIdAccessEnabled"?: boolean // When enabled, embedded visitors presenting a Source ID (verified per the verify mode below) may chat without going through the login flow. Disabled keeps every existing behavior unchanged. (Optional)
      "sourceIdVerifyMode"?:  // "Require signature verification": the embedding site must send an HMAC signature computed on its backend. "Source ID only": anyone knowing a Source ID can act as that contact; suitable only for testing or closed networks.

* `signature` - Require signature verification
* `raw` - Source ID only (Optional)
      {
      }
      "sourceIdSignatureTtlMinutes"?:  // How long a signature can be used to obtain the contact identity. Not a session lifetime: once exchanged, the session follows the existing WebChat mechanism.

* `1` - 1
* `5` - 5
* `15` - 15 (Optional)
      {
      }
      "keycloakTokenPassthrough"?: boolean // When enabled, the Keycloak access token a visitor logged in with is forwarded as the Authorization header when the AI calls MCP and API tools on their behalf, so tools do not require a separate per-tool login. Only for the Keycloak sign source. Never overrides an Authorization header a tool already resolves from its own credentials. The visitor identity is the organization member bound to the conversation at login, never the contact Source ID. (Optional)
      "keycloakTokenPassthroughAllowedHosts"?: [ // Hostnames (or "*.example.com" wildcards) of the tools the visitor Keycloak token may be sent to (Optional)
        string
      ]
      "organizationLogin": object // How visitors sign in when the sign source is "organization" (mirrors the back-office login); null for other sign sources
    }
    "enableAnalysis"?: boolean // Optional
  }
  }
  "chatbot": object // Chatbot (Agent) summary from the conversation's inbox, already eager-loaded.

``is_maigpt`` lets the client tell the default MaiGPT agent apart (icon, feature
gate) without comparing against the organization's ``maigpt_inbox`` id;
``file_workspace_mode`` is the effective P6 mode (MaiGPT, blueprint and the
organization's file-library switch already applied).
  "title": string
  "lastMessage"?: { // Optional
  {
    "id": string (uuid)
    "type"?: string // Optional
    "content"?: string // Optional
    "createdAt": string (timestamp)
  }
  }
  "callRecord": 
  {
    "id": string (uuid)
    "status"?: string (enum: initiated, ringing, in-progress, completed, failed, busy, no-answer, canceled) // * `initiated` - Initiated
* `ringing` - Ringing
* `in-progress` - In Progress
* `completed` - Completed
* `failed` - Failed
* `busy` - Busy
* `no-answer` - No Answer
* `canceled` - Canceled (Optional)
    "direction": string (enum: inbound, outbound) // * `inbound` - Inbound
* `outbound` - Outbound
    "isActive": boolean
    "canHangup": boolean
  }
  "lastMessageCreatedAt": string (timestamp)
  "unreadMessagesCount": integer
  "autoReplyEnabled": boolean
  "isAutoReplyNow": boolean
  "willAiReplyNow": boolean // Whether a text message arriving through the inbox gets an AI reply right now.

The messaging channels are gated by ``is_auto_reply_now`` (auto reply on and inside the
inbox auto-reply schedule) and by the inbox having a chatbot. The chatbot's API web chat
is answered by the completions endpoint, which ignores the schedule, so there the
conversation switch alone decides. The channel is only looked up for a web inbox outside
its schedule.

Surfaces that ignore the schedule on any inbox answer from their own rule instead: the
completions endpoint continuing an existing conversation (``completions_progress_status``)
and voice turns, which the voice agent answers.
  "lastReadAt": string (timestamp)
  "createdAt": string (timestamp)
  "isGroupChat": boolean
  "enableGroupMention"?: boolean // Optional
  "queryMetadata"?: { // Query metadata that controls the scope of searchable knowledge; see each endpoint's documentation (Optional)
  {
    "knowledgeBases"?: [ // List of searchable knowledge bases; an empty array or null denies access to all, while omission imposes no restriction (Optional)
      {
        "knowledgeBaseId": string (uuid) // Knowledge base ID
        "chatbotFileIds"?: [ // List of file IDs (excluded or exclusively selected, depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "knowledgeBaseFileIds"?: [ // List of file IDs (the new name for chatbotFileIds; this field takes precedence when both are provided) (Optional)
          string (uuid)
        ]
        "faqIds"?: [ // List of FAQ IDs (excluded or exclusively selected, depending on hasUserSelectedAll) (Optional)
          string (uuid)
        ]
        "hasUserSelectedAll"?: boolean // true selects all knowledge base content except the listed items; false selects only the listed items (Optional)
      }
    ]
    "labelRelations"?: { // Tag filters; an empty conditions array applies no tag filtering (it does not deny access). Must be provided together with knowledgeBases; when provided alone, knowledge base retrieval does not apply tag filtering (equivalent to no restriction) (Optional)
    {
      "operator": string (enum: AND, OR) // How tag conditions are combined
      "conditions"?: [ // List of tag conditions; supports nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "toolMask"?: object // Enabled/disabled state per tool: {tool_id: bool} (Optional)
  "connectorMask"?: object // Enabled/disabled state per connector: {connector_id: bool} (Optional)
  "thinkingConfig"?: object // None = not set (inherit the chatbot setting); otherwise {"reasoning_effort": <level>} (Optional)
  "thinkingConfigSchema": { // Thinking configuration form for this model; null means the model has no thinking configuration
  {
    "type"?: string // Form type, always reasoning_effort (Optional)
    "fields"?: object // reasoning_effort field: options lists the thinking levels this model accepts (from lowest to highest); default is always null (Optional)
    "canDisable"?: boolean // Whether thinking can be disabled for this model; when true, options includes off (Optional)
    "unsetBehavior"?: string (enum: off, model_default) // Actual behavior when neither the conversation nor the assistant has a thinking level set: off disables thinking (fastest); model_default lets the model decide, or enables thinking at the platform's default level (Optional)
    "unsetLevel"?: string // Level the platform actually sends when no level is set; null omits the thinking parameter and uses the model's own default (Optional)
  }
  }
  "effectiveThinkingConfig": object // The level the next reply sends on this conversation's model, after gating and clamping.

The stored choice resolves conversation > chatbot > the LLM row's default level; None means
the model sends no thinking parameter, so the provider default applies.
  "llm": string (uuid) // The model the next reply runs on: the conversation's pin, else its assistant's current model.

TC-0205: returning the stored ``conversation.llm`` would show a model an unpinned conversation no
longer uses once its assistant switches model, while ``thinking_config_schema`` already follows the
effective model. Read-only: displaying it never marks the conversation as explicitly selected;
only the update-llm endpoint does.
  "deepResearchStatus":  // Status of deep research for this conversation

* `not_used` - Not Used
* `started` - Started
* `running` - Running
* `completed` - Completed
* `failed` - Failed
  {
  }
  "deepResearchOutputFormat": // May have different types
  string (enum: canvas, chat, file) // Chosen delivery form for the finished report. Set only when the user clicks a format in the pre-research choice card; reset to NULL each time Deep Research is (re-)armed. NULL means no explicit choice (or a plan-skipping request) and falls back to Canvas at render time, but stays distinct from an explicit canvas pick so the frontend can lock the card only after a real selection. To roll back to fixed-Canvas behavior, force this to NULL/canvas — no redeploy needed.

* `canvas` - Canvas
* `chat` - Chat reply
* `file` - Downloadable file
  "ipAddress": string
  "ipCountryCode": string
  "ipRecordedAt": string (timestamp)
  "assignee": 
  {
    "id": string (uuid)
    "name": string
    "userId": string (uuid)
  }
  "status"?: string (enum: open, resolved, queued) // * `open` - Open
* `resolved` - Resolved
* `queued` - Queued (Optional)
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "memberIds"?: [ // Optional
        string (uuid)
      ]
    }
  ]
  "progressStatus": string
  "firstResponseAt"?: string (timestamp) // Optional
  "queuedAt"?: string (timestamp) // Optional
  "assignedAt"?: string (timestamp) // Optional
  "transferReason"?: string // Optional
  "satisfactionScore": integer // Conversation satisfaction rating on a 5-point scale (1=very dissatisfied, 5=very satisfied)
  "satisfactionComment": string
  "satisfactionSubmittedAt": string (timestamp)
  "latestAnalysisSummary": string // The conversation's current analysis summary (its active summary revision).

Reads the `latest_analysis_summary` annotation added by `setup_eager_loading`
to avoid an N+1 query. All read paths (list / retrieve / create) eager-load,
so the annotation is present; it falls back to '' only for instances built
outside `setup_eager_loading`.
  "clientPlatform":  // Client platform that created this conversation (web, desktop)

* `web` - Web
* `desktop` - Desktop
  {
  }
  "workingDirectory": string // Absolute path of the local folder bound to a desktop conversation. Only set for desktop conversations; null for web conversations.
  "slideProjectId": string
  "matchedField": string // Cached per instance: ``get_matched_message_id`` / ``get_matched_snippet``
both read this, so resolving it once avoids recomputing the keyword and
title-match logic three times per serialized conversation.
  "matchedSnippet": string // Keyword-centred excerpt of the matched message, with ellipses.

Reads the ``matched_message_content`` annotation added by
``ConversationViewSet._annotate_keyword_match``; no extra query.
  "matchedMessageId": string
  "pinnedAt": string (timestamp) // When the conversation was pinned. Null means unpinned.
  "folder": string (uuid) // The user-defined sidebar folder this conversation is grouped into. Null means unclassified (shown under the time groups). Deleting the folder resets this to null without deleting the conversation.
  "hasActiveBrowserSession": boolean // Whether a browser_use session snapshot exists to resume.

Lets the frontend know it should proactively re-request the browser
panel (emit ``browser:start``) after a page reload, instead of only
ever showing the panel when a live ``browser:frame`` push happens to
arrive first.
  "callerOrganizationId": string // Bound caller organization (stamped at conversation start); null when unbound
  "handoff": object // Handoff information: enabled is the platform toggle; the continuing conversation includes source_conversation_id/source_title; the original conversation being handed off includes continued_to_conversation_id/continued_to_title and latest_summary_message_id
  "displayContextUsage": boolean // Whether to show the context usage percentage bar (conversation platform toggle; always true for MaiGPT; independent of the handoff toggle)
  "autoSplitReason": string // Reason this conversation was created by automatic splitting: resolved (after resolution) / idle (inactivity); null if not created by splitting
  "continuedFrom": object // Conversation from which this split conversation continues: {id, last_message_at, deleted}; if the old conversation was deleted, id=null and deleted=true
  "continuedTo": object // Latest conversation split from this conversation: {id, created_at}; null if none
  "continuationSummary": object // Continuation summary: {state: disabled|pending|ready|not_applied, items: [{conversation_id, content, generated_at, last_message_at, old_status}]} (items ordered newest to oldest); null if not created by splitting
  "conversationKind": string // chat = a chat with an agent; secretary = the member's personal secretary Work chat
}
```

**Example Response Value**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "contact": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "avatar": "Response string",
    "email": "response@example.com",
    "phoneNumber": "Response string",
    "metadata": null,
    "sourceId": "Response string",
    "tags": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string"
      }
    ]
  },
  "inbox": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "channelType": "line",
    "unreadConversationsCount": 456,
    "signAuth": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "signSource": "550e8400-e29b-41d4-a716-446655440000",
      "signParams": {
        "keycloak": {
          "clientId": "Response string",
          "url": "Response string",
          "realm": "Response string"
        },
        "line": {
          "liffId": "Response string"
        },
        "ad": {
          "saml": "Response string"
        },
        "google": {
          "enabled": false
        }
      },
      "sourceIdAccessEnabled": false,
      "sourceIdVerifyMode": {},
      "sourceIdSignatureTtlMinutes": {},
      "keycloakTokenPassthrough": false,
      "keycloakTokenPassthroughAllowedHosts": [
        "Response string"
      ],
      "organizationLogin": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      }
    },
    "enableAnalysis": false
  },
  "chatbot": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "title": "Response string",
  "lastMessage": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": "Response string",
    "content": "Response string",
    "createdAt": "Response string"
  },
  "callRecord": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "initiated",
    "direction": "inbound",
    "isActive": false,
    "canHangup": false
  },
  "lastMessageCreatedAt": "Response string",
  "unreadMessagesCount": 456,
  "autoReplyEnabled": false,
  "isAutoReplyNow": false,
  "willAiReplyNow": false,
  "lastReadAt": "Response string",
  "createdAt": "Response string",
  "isGroupChat": false,
  "enableGroupMention": false,
  "queryMetadata": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "toolMask": null,
  "connectorMask": null,
  "thinkingConfig": null,
  "thinkingConfigSchema": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "effectiveThinkingConfig": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "llm": "550e8400-e29b-41d4-a716-446655440000",
  "deepResearchStatus": {},
  "deepResearchOutputFormat": "canvas",
  "ipAddress": "Response string",
  "ipCountryCode": "Response string",
  "ipRecordedAt": "Response string",
  "assignee": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string",
    "userId": "550e8400-e29b-41d4-a716-446655440000"
  },
  "status": "open",
  "tags": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string",
      "memberIds": [
        "550e8400-e29b-41d4-a716-446655440000"
      ]
    }
  ],
  "progressStatus": "Response string",
  "firstResponseAt": "Response string",
  "queuedAt": "Response string",
  "assignedAt": "Response string",
  "transferReason": "Response string",
  "satisfactionScore": 456,
  "satisfactionComment": "Response string",
  "satisfactionSubmittedAt": "Response string",
  "latestAnalysisSummary": "Response string",
  "clientPlatform": {},
  "workingDirectory": "Response string",
  "slideProjectId": "Response string",
  "matchedField": "Response string",
  "matchedSnippet": "Response string",
  "matchedMessageId": "Response string",
  "pinnedAt": "Response string",
  "folder": "550e8400-e29b-41d4-a716-446655440000",
  "hasActiveBrowserSession": false,
  "callerOrganizationId": "Response string",
  "handoff": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "displayContextUsage": false,
  "autoSplitReason": "Response string",
  "continuedFrom": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "continuedTo": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "continuationSummary": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "conversationKind": "Response string"
}
```

**Status Code: 409 - Agent context not ready: \`detail\` plus \`agentContext\` (pending / failed writes)**

***

### List Conversation Output Files <a href="#conversations-outputs-retrieve" id="conversations-outputs-retrieve"></a>

GET `/api/v1/conversations/{conversationId}/outputs/`

{% hint style="info" %}
For response fields (`count`, `source`, `results[]`), visibility, and sorting, see [Conversation Outputs API](https://docs.maiagent.ai/tech/api-integration/endpoint-behaviors/conversation-outputs-api) in the technical manual.
{% endhint %}

#### Parameters

| Parameter Name   | Required | Type   | Description |
| ---------------- | -------- | ------ | ----------- |
| `conversationId` | ✅        | string |             |

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API Call Example (Shell)
curl -X GET "https://api.maiagent.ai/api/v1/conversations/{conversationId}/outputs/" \
  -H "Authorization: Api-Key YOUR_API_KEY"

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY'
  }
};

axios.get("https://api.maiagent.ai/api/v1/conversations/{conversationId}/outputs/", config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request failed:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/conversations/{conversationId}/outputs/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY"
}


response = requests.get(url, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request failed:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->get("https://api.maiagent.ai/api/v1/conversations/{conversationId}/outputs/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request failed: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

| Status Code | Description                                                        |
| ----------- | ------------------------------------------------------------------ |
| 200         | No response body                                                   |
| 4XX         | Error response; X-Error-Code carries its code (see "Error codes"). |

***

### Share a Conversation <a href="#conversations-share-create" id="conversations-share-create"></a>

POST `/api/v1/conversations/{conversationPk}/share/`

#### Parameters

| Parameter Name   | Required | Type   | Description |
| ---------------- | -------- | ------ | ----------- |
| `conversationPk` | ✅        | string |             |

#### Request Body

**Request Parameters**

| Field       | Type                                       | Required | Description                                                       |
| ----------- | ------------------------------------------ | -------- | ----------------------------------------------------------------- |
| shareScope  | string (enum: organization, group, member) | Yes      | `organization`: Organization ; `group`: Group ; `member`: Member; |
| permission  | object                                     | No       |                                                                   |
| title       | string                                     | No       |                                                                   |
| description | string                                     | No       |                                                                   |
| groupIds    | array\[string]                             | No       |                                                                   |
| memberIds   | array\[string]                             | No       |                                                                   |

**Example Request Schema**

```typescript
{
  "shareScope": string (enum: organization, group, member) // * `organization` - Organization
* `group` - Group
* `member` - Member
  "permission"?:  // Optional
  {
  }
  "title"?: string // Optional
  "description"?: string // Optional
  "groupIds"?: [ // Optional
    string (uuid)
  ]
  "memberIds"?: [ // Optional
    string (uuid)
  ]
}
```

**Example Request Values**

```json
{
  "shareScope": "organization",
  "permission": {},
  "title": "Example name",
  "description": "Example string",
  "groupIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "memberIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ]
}
```

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X POST "https://api.maiagent.ai/api/v1/conversations/{conversationPk}/share/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "shareScope": "organization",
    "permission": {},
    "title": "Example name",
    "description": "Example string",
    "groupIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ],
    "memberIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ]
  }'

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
};

// Request Body (payload)
const data = {
    "shareScope": "organization",
    "permission": {},
    "title": "Example name",
    "description": "Example string",
    "groupIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ],
    "memberIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ]
  };

axios.post("https://api.maiagent.ai/api/v1/conversations/{conversationPk}/share/", data, config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/conversations/{conversationPk}/share/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request Body (payload)
data = {
    "shareScope": "organization",
    "permission": {},
    "title": "Example name",
    "description": "Example string",
    "groupIds": [
        "550e8400-e29b-41d4-a716-446655440000"
    ],
    "memberIds": [
        "550e8400-e29b-41d4-a716-446655440000"
    ]
}

response = requests.post(url, json=data, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->post("https://api.maiagent.ai/api/v1/conversations/{conversationPk}/share/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'shareScope' => 'organization',
            'permission' => new \stdClass(),
            'title' => 'Example name',
            'description' => 'Example string',
            'groupIds' => [
                '550e8400-e29b-41d4-a716-446655440000'
            ],
            'memberIds' => [
                '550e8400-e29b-41d4-a716-446655440000'
            ]
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 201**

**Example Response Schema**

```typescript
{
  "shareScope": string (enum: organization, group, member) // * `organization` - Organization
* `group` - Group
* `member` - Member
  "permission"?:  // Optional
  {
  }
  "title"?: string // Optional
  "description"?: string // Optional
  "groupIds"?: [ // Optional
    string (uuid)
  ]
  "memberIds"?: [ // Optional
    string (uuid)
  ]
}
```

**Example Response Values**

```json
{
  "shareScope": "organization",
  "permission": {},
  "title": "Response string",
  "description": "Response string",
  "groupIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "memberIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ]
}
```

**Status Code: 4XX - Error response; X-Error-Code carries its code (see "Error codes").**

***

### Quick Share a Conversation <a href="#conversations-share-quick-create" id="conversations-share-quick-create"></a>

POST `/api/v1/conversations/{conversationPk}/share/quick/`

#### Parameters

| Parameter Name   | Required | Type   | Description |
| ---------------- | -------- | ------ | ----------- |
| `conversationPk` | ✅        | string |             |

#### Request Body

**Request Parameters**

| Field       | Type                                       | Required | Description                                                       |
| ----------- | ------------------------------------------ | -------- | ----------------------------------------------------------------- |
| shareScope  | string (enum: organization, group, member) | Yes      | `organization`: Organization ; `group`: Group ; `member`: Member; |
| permission  | object                                     | No       |                                                                   |
| title       | string                                     | No       |                                                                   |
| description | string                                     | No       |                                                                   |
| groupIds    | array\[string]                             | No       |                                                                   |
| memberIds   | array\[string]                             | No       |                                                                   |

**Example Request Schema**

```typescript
{
  "shareScope": string (enum: organization, group, member) // * `organization` - Organization
* `group` - Group
* `member` - Member
  "permission"?:  // Optional
  {
  }
  "title"?: string // Optional
  "description"?: string // Optional
  "groupIds"?: [ // Optional
    string (uuid)
  ]
  "memberIds"?: [ // Optional
    string (uuid)
  ]
}
```

**Example Request Values**

```json
{
  "shareScope": "organization",
  "permission": {},
  "title": "Example name",
  "description": "Example string",
  "groupIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "memberIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ]
}
```

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X POST "https://api.maiagent.ai/api/v1/conversations/{conversationPk}/share/quick/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "shareScope": "organization",
    "permission": {},
    "title": "Example name",
    "description": "Example string",
    "groupIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ],
    "memberIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ]
  }'

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
};

// Request Body (payload)
const data = {
    "shareScope": "organization",
    "permission": {},
    "title": "Example name",
    "description": "Example string",
    "groupIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ],
    "memberIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ]
  };

axios.post("https://api.maiagent.ai/api/v1/conversations/{conversationPk}/share/quick/", data, config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/conversations/{conversationPk}/share/quick/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request Body (payload)
data = {
    "shareScope": "organization",
    "permission": {},
    "title": "Example name",
    "description": "Example string",
    "groupIds": [
        "550e8400-e29b-41d4-a716-446655440000"
    ],
    "memberIds": [
        "550e8400-e29b-41d4-a716-446655440000"
    ]
}

response = requests.post(url, json=data, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->post("https://api.maiagent.ai/api/v1/conversations/{conversationPk}/share/quick/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'shareScope' => 'organization',
            'permission' => new \stdClass(),
            'title' => 'Example name',
            'description' => 'Example string',
            'groupIds' => [
                '550e8400-e29b-41d4-a716-446655440000'
            ],
            'memberIds' => [
                '550e8400-e29b-41d4-a716-446655440000'
            ]
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 200**

**Example Response Schema**

```typescript
{
  "shareScope": string (enum: organization, group, member) // * `organization` - Organization
* `group` - Group
* `member` - Member
  "permission"?:  // Optional
  {
  }
  "title"?: string // Optional
  "description"?: string // Optional
  "groupIds"?: [ // Optional
    string (uuid)
  ]
  "memberIds"?: [ // Optional
    string (uuid)
  ]
}
```

**Example Response Values**

```json
{
  "shareScope": "organization",
  "permission": {},
  "title": "Response string",
  "description": "Response string",
  "groupIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "memberIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ]
}
```

**Status Code: 4XX - Error response; X-Error-Code carries its code (see "Error codes").**

***

### List Shared Conversations <a href="#shared-conversations-list" id="shared-conversations-list"></a>

GET `/api/v1/shared-conversations/`

#### Parameters

| Parameter Name | Required | Type    | Description                                    |
| -------------- | -------- | ------- | ---------------------------------------------- |
| `page`         | ❌        | integer | A page number within the paginated result set. |
| `pageSize`     | ❌        | integer | Number of results to return per page.          |

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X GET "https://api.maiagent.ai/api/v1/shared-conversations/?page=1&pageSize=1" \
  -H "Authorization: Api-Key YOUR_API_KEY"

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY'
  }
};

axios.get("https://api.maiagent.ai/api/v1/shared-conversations/?page=1&pageSize=1", config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/shared-conversations/?page=1&pageSize=1"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY"
}


response = requests.get(url, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->get("https://api.maiagent.ai/api/v1/shared-conversations/?page=1&pageSize=1", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 200**

**Example Response Schema**

```typescript
{
  "count": integer
  "next"?: string (uri) // Optional
  "previous"?: string (uri) // Optional
  "results": [
    {
      "id": string (uuid)
      "title": string // Custom title for the shared conversation
      "description": string // Custom description for the shared conversation
      "shareScope": 
      {
      }
      "permission": 
      {
      }
      "sharedBy": 
      {
        "id": string (uuid)
        "name": string
      }
      "isOwner": boolean
      "messageCount": integer // Messages the share shows; the view counts a whole page at once (``context['message_counts']``).
      "createdAt": string (timestamp)
    }
  ]
}
```

**Example Response Values**

```json
{
  "count": 123,
  "next": "http://api.example.org/accounts/?page=4",
  "previous": "http://api.example.org/accounts/?page=2",
  "results": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "title": "Response string",
      "description": "Response string",
      "shareScope": {},
      "permission": {},
      "sharedBy": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string"
      },
      "isOwner": false,
      "messageCount": 456,
      "createdAt": "Response string"
    }
  ]
}
```

***

### Retrieve a Shared Conversation <a href="#shared-conversations-retrieve" id="shared-conversations-retrieve"></a>

GET `/api/v1/shared-conversations/{id}/`

#### Parameters

| Parameter Name | Required | Type   | Description                                         |
| -------------- | -------- | ------ | --------------------------------------------------- |
| `id`           | ✅        | string | A UUID string identifying this Shared Conversation. |

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X GET "https://api.maiagent.ai/api/v1/shared-conversations/550e8400-e29b-41d4-a716-446655440000/" \
  -H "Authorization: Api-Key YOUR_API_KEY"

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY'
  }
};

axios.get("https://api.maiagent.ai/api/v1/shared-conversations/550e8400-e29b-41d4-a716-446655440000/", config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/shared-conversations/550e8400-e29b-41d4-a716-446655440000/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY"
}


response = requests.get(url, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->get("https://api.maiagent.ai/api/v1/shared-conversations/550e8400-e29b-41d4-a716-446655440000/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 200**

**Example Response Schema**

```typescript
{
  "id": string (uuid)
  "title": string // Custom title for the shared conversation
  "description": string // Custom description for the shared conversation
  "shareScope": 
  {
  }
  "permission": 
  {
  }
  "sharedBy": 
  {
    "id": string (uuid)
    "name": string
  }
  "isOwner": boolean
  "conversationId": string (uuid) // The original conversation, exposed only to the sharer while the share still follows it ("continue").
  "groupIds": [ // Current recipients, for the sharer's settings dialog only.
    string (uuid)
  ]
  "memberIds": [ // Recipients that are still active members, so saving the dialog back as-is stays valid.
    string (uuid)
  ]
  "needsContinueReminder": boolean // True when the sharer can continue the original conversation and has not been warned yet.
  "createdAt": string (timestamp)
  "conversationSnapshot": object
}
```

**Example Response Values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "title": "Response string",
  "description": "Response string",
  "shareScope": {},
  "permission": {},
  "sharedBy": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string"
  },
  "isOwner": false,
  "conversationId": "550e8400-e29b-41d4-a716-446655440000",
  "groupIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "memberIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "needsContinueReminder": false,
  "createdAt": "Response string",
  "conversationSnapshot": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  }
}
```

***

### Update a Shared Conversation <a href="#shared-conversations-partial-update" id="shared-conversations-partial-update"></a>

PATCH `/api/v1/shared-conversations/{id}/`

#### Parameters

| Parameter Name | Required | Type   | Description                                         |
| -------------- | -------- | ------ | --------------------------------------------------- |
| `id`           | ✅        | string | A UUID string identifying this Shared Conversation. |

#### Request Body

**Request Parameters**

| Field       | Type                                       | Required | Description                                                       |
| ----------- | ------------------------------------------ | -------- | ----------------------------------------------------------------- |
| title       | string                                     | No       |                                                                   |
| description | string                                     | No       |                                                                   |
| permission  | string (enum: readonly, copy)              | No       | `readonly`: Read Only ; `copy`: Copyable;                         |
| shareScope  | string (enum: organization, group, member) | No       | `organization`: Organization ; `group`: Group ; `member`: Member; |
| groupIds    | array\[string]                             | No       |                                                                   |
| memberIds   | array\[string]                             | No       |                                                                   |

**Example Request Schema**

```typescript
{
  "title"?: string // Optional
  "description"?: string // Optional
  "permission"?: string (enum: readonly, copy) // * `readonly` - Read Only
* `copy` - Copyable (Optional)
  "shareScope"?: string (enum: organization, group, member) // * `organization` - Organization
* `group` - Group
* `member` - Member (Optional)
  "groupIds"?: [ // Optional
    string (uuid)
  ]
  "memberIds"?: [ // Optional
    string (uuid)
  ]
}
```

**Example Request Values**

```json
{
  "title": "Example name",
  "description": "Example string",
  "permission": "readonly",
  "shareScope": "organization",
  "groupIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "memberIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ]
}
```

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X PATCH "https://api.maiagent.ai/api/v1/shared-conversations/550e8400-e29b-41d4-a716-446655440000/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Example name",
    "description": "Example string",
    "permission": "readonly",
    "shareScope": "organization",
    "groupIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ],
    "memberIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ]
  }'

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
};

// Request body (payload)
const data = {
    "title": "Example name",
    "description": "Example string",
    "permission": "readonly",
    "shareScope": "organization",
    "groupIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ],
    "memberIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ]
  };

axios.patch("https://api.maiagent.ai/api/v1/shared-conversations/550e8400-e29b-41d4-a716-446655440000/", data, config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request failed:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/shared-conversations/550e8400-e29b-41d4-a716-446655440000/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request body (payload)
data = {
    "title": "Example name",
    "description": "Example string",
    "permission": "readonly",
    "shareScope": "organization",
    "groupIds": [
        "550e8400-e29b-41d4-a716-446655440000"
    ],
    "memberIds": [
        "550e8400-e29b-41d4-a716-446655440000"
    ]
}

response = requests.patch(url, json=data, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request failed:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->patch("https://api.maiagent.ai/api/v1/shared-conversations/550e8400-e29b-41d4-a716-446655440000/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'title' => 'Example name',
            'description' => 'Example string',
            'permission' => 'readonly',
            'shareScope' => 'organization',
            'groupIds' => [
                '550e8400-e29b-41d4-a716-446655440000'
            ],
            'memberIds' => [
                '550e8400-e29b-41d4-a716-446655440000'
            ]
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request failed: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status code: 200**

**Example Response Schema**

```typescript
{
  "title"?: string // Optional
  "description"?: string // Optional
  "permission"?: string (enum: readonly, copy) // * `readonly` - Read Only
* `copy` - Copyable (Optional)
  "shareScope"?: string (enum: organization, group, member) // * `organization` - Organization
* `group` - Group
* `member` - Member (Optional)
  "groupIds"?: [ // Optional
    string (uuid)
  ]
  "memberIds"?: [ // Optional
    string (uuid)
  ]
}
```

**Example Response Values**

```json
{
  "title": "Response string",
  "description": "Response string",
  "permission": "readonly",
  "shareScope": "organization",
  "groupIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "memberIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ]
}
```

***

### Delete a Shared Conversation <a href="#shared-conversations-destroy" id="shared-conversations-destroy"></a>

DELETE `/api/v1/shared-conversations/{id}/`

#### Parameters

| Parameter Name | Required | Type   | Description                                         |
| -------------- | -------- | ------ | --------------------------------------------------- |
| `id`           | ✅        | string | A UUID string identifying this Shared Conversation. |

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# Example API Call (Shell)
curl -X DELETE "https://api.maiagent.ai/api/v1/shared-conversations/550e8400-e29b-41d4-a716-446655440000/" \
  -H "Authorization: Api-Key YOUR_API_KEY"

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY'
  }
};

// Request body (payload)
const data = null;

axios.delete("https://api.maiagent.ai/api/v1/shared-conversations/550e8400-e29b-41d4-a716-446655440000/", data, config)
  .then(response => {
    console.log('Response retrieved successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/shared-conversations/550e8400-e29b-41d4-a716-446655440000/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY"
}


response = requests.delete(url, headers=headers)
try:
    print("Response retrieved successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->delete("https://api.maiagent.ai/api/v1/shared-conversations/550e8400-e29b-41d4-a716-446655440000/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response retrieved successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

| Status Code | Description      |
| ----------- | ---------------- |
| 204         | No response body |

***

### Fork a Shared Conversation <a href="#shared-conversations-fork-create" id="shared-conversations-fork-create"></a>

POST `/api/v1/shared-conversations/{id}/fork/`

#### Parameters

| Parameter Name | Required | Type   | Description                                         |
| -------------- | -------- | ------ | --------------------------------------------------- |
| `id`           | ✅        | string | A UUID string identifying this Shared Conversation. |

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API Call Example (Shell)
curl -X POST "https://api.maiagent.ai/api/v1/shared-conversations/550e8400-e29b-41d4-a716-446655440000/fork/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
};

// Request body (payload)
const data = {};

axios.post("https://api.maiagent.ai/api/v1/shared-conversations/550e8400-e29b-41d4-a716-446655440000/fork/", data, config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/shared-conversations/550e8400-e29b-41d4-a716-446655440000/fork/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request body (payload)
data = {}

response = requests.post(url, json=data, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->post("https://api.maiagent.ai/api/v1/shared-conversations/550e8400-e29b-41d4-a716-446655440000/fork/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => new \stdClass()
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 200**

**Example Response Schema**

```typescript
{
  "id": string (uuid)
  "title": string // Custom title for the shared conversation
  "description": string // Custom description for the shared conversation
  "shareScope": 
  {
  }
  "permission": 
  {
  }
  "sharedBy": 
  {
    "id": string (uuid)
    "name": string
  }
  "isOwner": boolean
  "conversationId": string (uuid) // The original conversation, exposed only to the sharer while the share still follows it ("continue").
  "groupIds": [ // Current recipients, for the sharer's settings dialog only.
    string (uuid)
  ]
  "memberIds": [ // Recipients that are still active members, so saving the dialog back as-is stays valid.
    string (uuid)
  ]
  "needsContinueReminder": boolean // True when the sharer can continue the original conversation and has not been warned yet.
  "createdAt": string (timestamp)
  "conversationSnapshot": object
}
```

**Example Response Values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "title": "Response string",
  "description": "Response string",
  "shareScope": {},
  "permission": {},
  "sharedBy": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string"
  },
  "isOwner": false,
  "conversationId": "550e8400-e29b-41d4-a716-446655440000",
  "groupIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "memberIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "needsContinueReminder": false,
  "createdAt": "Response string",
  "conversationSnapshot": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  }
}
```

***

### Retrieve My Shared Conversations <a href="#shared-conversations-my-shares-retrieve" id="shared-conversations-my-shares-retrieve"></a>

GET `/api/v1/shared-conversations/my-shares/`

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API Call Example (Shell)
curl -X GET "https://api.maiagent.ai/api/v1/shared-conversations/my-shares/" \
  -H "Authorization: Api-Key YOUR_API_KEY"

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY'
  }
};

axios.get("https://api.maiagent.ai/api/v1/shared-conversations/my-shares/", config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/shared-conversations/my-shares/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY"
}


response = requests.get(url, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->get("https://api.maiagent.ai/api/v1/shared-conversations/my-shares/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 200**

**Example Response Schema**

```typescript
{
  "id": string (uuid)
  "title": string // Custom title for the shared conversation
  "description": string // Custom description for the shared conversation
  "shareScope": 
  {
  }
  "permission": 
  {
  }
  "sharedBy": 
  {
    "id": string (uuid)
    "name": string
  }
  "isOwner": boolean
  "messageCount": integer // Messages the share shows; the view counts a whole page at once (``context['message_counts']``).
  "createdAt": string (timestamp)
}
```

**Example Response Values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "title": "Response string",
  "description": "Response string",
  "shareScope": {},
  "permission": {},
  "sharedBy": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string"
  },
  "isOwner": false,
  "messageCount": 456,
  "createdAt": "Response string"
}
```

***

### Create Message Feedback <a href="#messages-feedback-create" id="messages-feedback-create"></a>

POST `/api/v1/messages/{messagePk}/feedback/`

#### Parameters

| Parameter Name | Required | Type   | Description |
| -------------- | -------- | ------ | ----------- |
| `messagePk`    | ✅        | string |             |

#### Request Body

**Request Parameters**

| Field      | Type   | Required | Description |
| ---------- | ------ | -------- | ----------- |
| type       | object | Yes      |             |
| suggestion | string | No       |             |

**Example Request Schema**

```typescript
{
  "type": 
  {
  }
  "suggestion"?: string // Optional
}
```

**Example Request Values**

```json
{
  "type": {},
  "suggestion": "example string"
}
```

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X POST "https://api.maiagent.ai/api/v1/messages/{messagePk}/feedback/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": {},
    "suggestion": "example string"
  }'

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
};

// Request body (payload)
const data = {
    "type": {},
    "suggestion": "example string"
  };

axios.post("https://api.maiagent.ai/api/v1/messages/{messagePk}/feedback/", data, config)
  .then(response => {
    console.log('Response retrieved successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/messages/{messagePk}/feedback/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request body (payload)
data = {
    "type": {},
    "suggestion": "example string"
}

response = requests.post(url, json=data, headers=headers)
try:
    print("Response retrieved successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->post("https://api.maiagent.ai/api/v1/messages/{messagePk}/feedback/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'type' => new \stdClass(),
            'suggestion' => 'example string'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response retrieved successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 201**

**Example Response Schema**

```typescript
{
  "id": string (uuid)
  "type": 
  {
  }
  "suggestion"?: string // Optional
  "updatedAt": string (timestamp)
}
```

**Example Response Values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "type": {},
  "suggestion": "response string",
  "updatedAt": "response string"
}
```

**Status Code: 400 - This message already has feedback. Use PATCH to update it.**

***

### Update Message Feedback <a href="#messages-feedback-update" id="messages-feedback-update"></a>

PUT `/api/v1/messages/{messagePk}/feedback/{id}/`

#### Parameters

| Parameter Name | Required | Type   | Description                                      |
| -------------- | -------- | ------ | ------------------------------------------------ |
| `id`           | ✅        | string | A UUID string identifying this Message Feedback. |
| `messagePk`    | ✅        | string |                                                  |

#### Request Body

**Request Parameters**

| Field      | Type   | Required | Description |
| ---------- | ------ | -------- | ----------- |
| type       | object | Yes      |             |
| suggestion | string | No       |             |

**Example Request Schema**

```typescript
{
  "type": 
  {
  }
  "suggestion"?: string // Optional
}
```

**Example Request Values**

```json
{
  "type": {},
  "suggestion": "example string"
}
```

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X PUT "https://api.maiagent.ai/api/v1/messages/{messagePk}/feedback/550e8400-e29b-41d4-a716-446655440000/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": {},
    "suggestion": "example string"
  }'

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
};

// Request body (payload)
const data = {
    "type": {},
    "suggestion": "example string"
  };

axios.put("https://api.maiagent.ai/api/v1/messages/{messagePk}/feedback/550e8400-e29b-41d4-a716-446655440000/", data, config)
  .then(response => {
    console.log('Response retrieved successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/messages/{messagePk}/feedback/550e8400-e29b-41d4-a716-446655440000/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request body (payload)
data = {
    "type": {},
    "suggestion": "example string"
}

response = requests.put(url, json=data, headers=headers)
try:
    print("Response retrieved successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->put("https://api.maiagent.ai/api/v1/messages/{messagePk}/feedback/550e8400-e29b-41d4-a716-446655440000/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'type' => new \stdClass(),
            'suggestion' => 'example string'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response retrieved successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 200**

**Example Response Schema**

```typescript
{
  "id": string (uuid)
  "type": 
  {
  }
  "suggestion"?: string // Optional
  "updatedAt": string (timestamp)
}
```

**Example Response Values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "type": {},
  "suggestion": "response string",
  "updatedAt": "response string"
}
```

***

### Delete Message Feedback <a href="#messages-feedback-destroy" id="messages-feedback-destroy"></a>

DELETE `/api/v1/messages/{messagePk}/feedback/{id}/`

#### Parameters

| Parameter Name | Required | Type   | Description                                      |
| -------------- | -------- | ------ | ------------------------------------------------ |
| `id`           | ✅        | string | A UUID string identifying this Message Feedback. |
| `messagePk`    | ✅        | string |                                                  |

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X DELETE "https://api.maiagent.ai/api/v1/messages/{messagePk}/feedback/550e8400-e29b-41d4-a716-446655440000/" \
  -H "Authorization: Api-Key YOUR_API_KEY"

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY'
  }
};

// Request body (payload)
const data = null;

axios.delete("https://api.maiagent.ai/api/v1/messages/{messagePk}/feedback/550e8400-e29b-41d4-a716-446655440000/", data, config)
  .then(response => {
    console.log('Response retrieved successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/messages/{messagePk}/feedback/550e8400-e29b-41d4-a716-446655440000/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY"
}


response = requests.delete(url, headers=headers)
try:
    print("Response retrieved successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->delete("https://api.maiagent.ai/api/v1/messages/{messagePk}/feedback/550e8400-e29b-41d4-a716-446655440000/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response retrieved successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

| Status Code | Description                                            |
| ----------- | ------------------------------------------------------ |
| 204         | Feedback deleted successfully                          |
| 400         | This feedback does not belong to the specified message |

***

### List conversation records <a href="#records-list" id="records-list"></a>

GET `/api/v1/records/`

#### Parameters

| Parameter name       | Required | Type    | Description                                                                                                                                                                                                                                                                                                                                      |
| -------------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `callerMember`       | ❌        | array   | Multiple values may be separated by commas.                                                                                                                                                                                                                                                                                                      |
| `chatbot`            | ❌        | string  | Filter by a specific AI assistant (UUID)                                                                                                                                                                                                                                                                                                         |
| `endDate`            | ❌        | string  | End date (format: YYYY-MM-DD, e.g., 2025-01-31)                                                                                                                                                                                                                                                                                                  |
| `endDatetime`        | ❌        | string  |                                                                                                                                                                                                                                                                                                                                                  |
| `errorType`          | ❌        | array   | Multiple values may be separated by commas. \`system\`: System ; \`llm\`: LLM ; \`embedding\`: Embedding ; \`reranker\`: Reranker ; \`vector\_db\`: Vector DB ; \`workflow\`: Workflow ; \`tool\`: Tool ; \`timeout\`: Ti...                                                                                                                     |
| `excludeBuiltIn`     | ❌        | boolean | When true, excludes data from official built-in Agents (locked instances created by renting from the Agent marketplace), retaining only assistants created by the organization. Copies created with "Copy as custom" count as custom assistants. Omitting this parameter or setting it to false leaves the results unfiltered by this condition. |
| `feedbackType`       | ❌        | array   | Multiple values may be separated by commas. \`like\`: Like ; \`dislike\`: Dislike;                                                                                                                                                                                                                                                               |
| `hasError`           | ❌        | boolean |                                                                                                                                                                                                                                                                                                                                                  |
| `hasFeedback`        | ❌        | boolean |                                                                                                                                                                                                                                                                                                                                                  |
| `keyword`            | ❌        | string  | Keyword search (searches both user messages and bot replies)                                                                                                                                                                                                                                                                                     |
| `largeLanguageModel` | ❌        | string  | Filter by a specific large language model (UUID)                                                                                                                                                                                                                                                                                                 |
| `page`               | ❌        | integer | A page number within the paginated result set.                                                                                                                                                                                                                                                                                                   |
| `pageSize`           | ❌        | integer | Number of results to return per page.                                                                                                                                                                                                                                                                                                            |
| `source`             | ❌        | string  | Filter by record source, separated by commas. Returns both sources when omitted. interactive = user conversations, scheduled = scheduled executions.                                                                                                                                                                                             |
| `startDate`          | ❌        | string  | Start date (format: YYYY-MM-DD, e.g., 2025-01-01)                                                                                                                                                                                                                                                                                                |
| `startDatetime`      | ❌        | string  |                                                                                                                                                                                                                                                                                                                                                  |
| `tool`               | ❌        | string  |                                                                                                                                                                                                                                                                                                                                                  |

#### Code examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X GET "https://api.maiagent.ai/api/v1/records/?callerMember=example&chatbot=example&endDate=example&endDatetime=2025-01-01T00:00:00.000Z&errorType=example&excludeBuiltIn=true&feedbackType=example&hasError=true&hasFeedback=true&keyword=example&largeLanguageModel=example&page=1&pageSize=1&source=interactive&startDate=example&startDatetime=2025-01-01T00:00:00.000Z&tool=example" \
  -H "Authorization: Api-Key YOUR_API_KEY"

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY'
  }
};

axios.get("https://api.maiagent.ai/api/v1/records/?callerMember=example&chatbot=example&endDate=example&endDatetime=2025-01-01T00:00:00.000Z&errorType=example&excludeBuiltIn=true&feedbackType=example&hasError=true&hasFeedback=true&keyword=example&largeLanguageModel=example&page=1&pageSize=1&source=interactive&startDate=example&startDatetime=2025-01-01T00:00:00.000Z&tool=example", config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/records/?callerMember=example&chatbot=example&endDate=example&endDatetime=2025-01-01T00:00:00.000Z&errorType=example&excludeBuiltIn=true&feedbackType=example&hasError=true&hasFeedback=true&keyword=example&largeLanguageModel=example&page=1&pageSize=1&source=interactive&startDate=example&startDatetime=2025-01-01T00:00:00.000Z&tool=example"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY"
}


response = requests.get(url, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->get("https://api.maiagent.ai/api/v1/records/?callerMember=example&chatbot=example&endDate=example&endDatetime=2025-01-01T00:00:00.000Z&errorType=example&excludeBuiltIn=true&feedbackType=example&hasError=true&hasFeedback=true&keyword=example&largeLanguageModel=example&page=1&pageSize=1&source=interactive&startDate=example&startDatetime=2025-01-01T00:00:00.000Z&tool=example", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response

**Status code: 200**

**Example response schema**

```typescript
{
  "count": integer
  "next"?: string (uri) // Optional
  "previous"?: string (uri) // Optional
  "results": [
    {
      "id": string (uuid)
      "senderName": string // Returns the name of the user message sender
      "callerMemberId": string
      "callerMemberName": string
      "feedback": 
      {
        "id": string (uuid)
        "type": 
        {
        }
        "suggestion"?: string // Optional
        "updatedAt": string (timestamp)
      }
      "inputMessage": string
      "condenseMessage": string // User message rewritten from the chat history and context.
      "outputMessage": string
      "faithfulnessScore": number (double) // Whether the chatbot reply is grounded in the knowledge base rather than made up.
      "displayFaithfulnessScore": integer
      "answerRelevancyScore": number (double) // Whether the chatbot reply is relevant to the user's question.
      "displayAnswerRelevancyScore": integer
      "contextPrecisionScore": number (double) // Whether the chatbot reply is relevant to the reference context.
      "displayContextPrecisionScore": integer
      "answerCorrectnessScore": number (double) // Whether the chatbot reply is correct (requires a ground-truth answer).
      "displayAnswerCorrectnessScore": integer
      "answerSimilarityScore": number (double) // Whether the chatbot reply is similar to the ground-truth answer.
      "displayAnswerSimilarityScore": integer
      "contextRecallScore": number (double) // Whether the chatbot reply recalls the relevant information from the reference context.
      "displayContextRecallScore": integer
      "replyTime": string
      "createdAt": string (timestamp)
      "error": string // Error message raised during execution.
      "errorType": // May have different types
      string (enum: system, llm, embedding, reranker, vector_db, workflow, tool, timeout, client_interrupt, hook_blocked, evaluation, other) // Error source: system, LLM, embedding, reranker, and so on.

* `system` - System
* `llm` - LLM
* `embedding` - Embedding
* `reranker` - Reranker
* `vector_db` - Vector DB
* `workflow` - Workflow
* `tool` - Tool
* `timeout` - Timeout
* `client_interrupt` - Client Interrupt
* `hook_blocked` - Hook Blocked
* `evaluation` - Evaluation
* `other` - Other
      "processingTime": object // Returns timing statistics for each processing stage, including the streaming_metrics waterfall
      "usage": 
      {
        "words":  // Word counts
        {
          "input": integer // User message word count (including the word equivalent calculated from the number of images)
          "output": integer // AI assistant reply word count (including the word equivalent calculated from the number of images)
          "total": integer // Sum of input and output
        }
        "tokens":  // token usage
        {
          "llm":  // LLM token usage
          {
            "input": integer // Number of LLM input tokens
            "output": integer // Number of LLM output tokens
            "cacheCreationInputTokens": integer // Number of input tokens written to the prompt cache
            "cacheReadInputTokens": integer // Number of input tokens read from the prompt cache
            "total": integer // Sum of input, output, and both cache token counts
          }
          "embedding": integer // Number of text embedding tokens
          "embeddingImage": integer // Number of image embedding tokens
          "reranker": integer // Number of chunks sent to the reranker
        }
        "llmCallBreakdown": [ // Token breakdown for each LLM call, ordered by call time; always an empty array on list endpoints. Use the detail endpoint instead
          {
            "sequence": integer // Sequence number of the LLM call within this reply, starting at 1
            "inputTokens": integer // Number of input tokens for this call
            "outputTokens": integer // Number of output tokens for this call
            "cacheCreationInputTokens": integer // Number of tokens written to the prompt cache by this call
            "cacheReadInputTokens": integer // Number of tokens read from the prompt cache by this call
            "processingTimeSecs": number (double) // Duration of this call in seconds; null if not recorded
            "ttftSecs"?: number (double) // Time to first token for this call in seconds; null if not recorded. Some older records omit this key (optional)
          }
        ]
      }
      "citationNodes": [
        {
          "id": string
          "text": string
          "chatbotFileId": string
          "fileName": string
          "url": string
        }
      ]
      "inputHookLogs": [
        {
          "id": string (uuid)
          "hook": string (uuid)
          "hookName": string
          "hookTypeLabel": string
          "hookAction": string
          "conversation": string (uuid)
          "message": string (uuid)
          "messageContent": string // Return the raw message text.
          "chatbotId": string
          "chatbotName": string
          "metadata": object
          "triggerPoint": string
          "createdAt": string (timestamp)
          "updatedAt": string (timestamp)
        }
      ]
      "outputHookLogs": [
        {
          "id": string (uuid)
          "hook": string (uuid)
          "hookName": string
          "hookTypeLabel": string
          "hookAction": string
          "conversation": string (uuid)
          "message": string (uuid)
          "messageContent": string // Return the raw message text.
          "chatbotId": string
          "chatbotName": string
          "metadata": object
          "triggerPoint": string
          "createdAt": string (timestamp)
          "updatedAt": string (timestamp)
        }
      ]
      "instructionId": string
      "llm": 
      {
        "id": string (uuid)
        "name": string
      }
      "effectiveMaxLlmOutputTokens": integer // max_tokens actually used for this reply, from Chatbot.custom_max_llm_output_tokens or LLM.max_tokens.
      "chatbot": 
      {
        "id": string (uuid)
        "name": string
      }
      "context": string
      "conversationId": string (uuid)
      "inboxId": string (uuid)
      "botMessageId": string (uuid)
      "userMessageId": string (uuid)
      "creditCosts": [ // Credit deductions for this reply, grouped by billing item. null if the organization does not use credit-based billing, or if it does but this reply has no deduction transactions
        {
          "itemCode": string // Billing item code
          "itemName": string // Billing item name
          "category": string // Billing item category
          "credits": number (double) // Total credits deducted for this billing item in this reply
          "quantity": number (double) // Total usage for this billing item (the unit depends on the item, e.g., token count)
          "unitPrice": number (double) // Snapshot of the unit price at the time of deduction; null if the unit price was not recorded
          "count": integer // Number of deduction transactions combined into this row
        }
      ]
      "cacheSavedCredits": number (double) // Net credits saved by prompt caching for this reply; ``None`` outside credit mode.

Reuses the usage-statistics saving semantics (``compute_cache_saved_credits``)
on the reply's own billing batch, so the per-reply figure and the hourly
aggregate are defined by the same formula. The net value may be negative
(cache-creation premium exceeding read savings); the frontend hides
non-positive values by design.
      "trace": object // Return the round trace timeline for the detail view; ``None`` for legacy records.
      "llmRawRequest": object // Raw messages array sent to the LLM on the final round, including system prompt and memory
      "source": string // Origin discriminator for the unified /records/ response.
      "schedule": object // Shape parity with scheduled rows, which link their owning ChatbotSchedule here.
      "voiceOrigin": object // The voice session and turn that delegated this question here, if it was a delegation.
    }
  ]
}
```

**Example response values**

```json
{
  "count": 123,
  "next": "http://api.example.org/accounts/?page=4",
  "previous": "http://api.example.org/accounts/?page=2",
  "results": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "senderName": "Response string",
      "callerMemberId": "Response string",
      "callerMemberName": "Response string",
      "feedback": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "type": {},
        "suggestion": "Response string",
        "updatedAt": "Response string"
      },
      "inputMessage": "Response string",
      "condenseMessage": "Response string",
      "outputMessage": "Response string",
      "faithfulnessScore": 456,
      "displayFaithfulnessScore": 456,
      "answerRelevancyScore": 456,
      "displayAnswerRelevancyScore": 456,
      "contextPrecisionScore": 456,
      "displayContextPrecisionScore": 456,
      "answerCorrectnessScore": 456,
      "displayAnswerCorrectnessScore": 456,
      "answerSimilarityScore": 456,
      "displayAnswerSimilarityScore": 456,
      "contextRecallScore": 456,
      "displayContextRecallScore": 456,
      "replyTime": "Response string",
      "createdAt": "Response string",
      "error": "Response string",
      "errorType": "system",
      "processingTime": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      },
      "usage": {
        "words": {
          "input": 456,
          "output": 456,
          "total": 456
        },
        "tokens": {
          "llm": {
            "input": 456,
            "output": 456,
            "cacheCreationInputTokens": 456,
            "cacheReadInputTokens": 456,
            "total": 456
          },
          "embedding": 456,
          "embeddingImage": 456,
          "reranker": 456
        },
        "llmCallBreakdown": [
          {
            "sequence": 456,
            "inputTokens": 456,
            "outputTokens": 456,
            "cacheCreationInputTokens": 456,
            "cacheReadInputTokens": 456,
            "processingTimeSecs": 456,
            "ttftSecs": 456
          }
        ]
      },
      "citationNodes": [
        {
          "id": "Response string",
          "text": "Response string",
          "chatbotFileId": "Response string",
          "fileName": "Response string",
          "url": "Response string"
        }
      ],
      "inputHookLogs": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "hook": "550e8400-e29b-41d4-a716-446655440000",
          "hookName": "Response string",
          "hookTypeLabel": "Response string",
          "hookAction": "Response string",
          "conversation": "550e8400-e29b-41d4-a716-446655440000",
          "message": "550e8400-e29b-41d4-a716-446655440000",
          "messageContent": "Response string",
          "chatbotId": "Response string",
          "chatbotName": "Response string",
          "metadata": null,
          "triggerPoint": "Response string",
          "createdAt": "Response string",
          "updatedAt": "Response string"
        }
      ],
      "outputHookLogs": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "hook": "550e8400-e29b-41d4-a716-446655440000",
          "hookName": "Response string",
          "hookTypeLabel": "Response string",
          "hookAction": "Response string",
          "conversation": "550e8400-e29b-41d4-a716-446655440000",
          "message": "550e8400-e29b-41d4-a716-446655440000",
          "messageContent": "Response string",
          "chatbotId": "Response string",
          "chatbotName": "Response string",
          "metadata": null,
          "triggerPoint": "Response string",
          "createdAt": "Response string",
          "updatedAt": "Response string"
        }
      ],
      "instructionId": "Response string",
      "llm": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string"
      },
      "effectiveMaxLlmOutputTokens": 456,
      "chatbot": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Response string"
      },
      "context": "Response string",
      "conversationId": "550e8400-e29b-41d4-a716-446655440000",
      "inboxId": "550e8400-e29b-41d4-a716-446655440000",
      "botMessageId": "550e8400-e29b-41d4-a716-446655440000",
      "userMessageId": "550e8400-e29b-41d4-a716-446655440000",
      "creditCosts": [
        {
          "itemCode": "Response string",
          "itemName": "Response string",
          "category": "Response string",
          "credits": 456,
          "quantity": 456,
          "unitPrice": 456,
          "count": 456
        }
      ],
      "cacheSavedCredits": 456,
      "trace": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      },
      "llmRawRequest": null,
      "source": "Response string",
      "schedule": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      },
      "voiceOrigin": {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Example response name",
        "description": "Example response description"
      }
    }
  ]
}
```

***

### Retrieve a conversation record <a href="#records-retrieve" id="records-retrieve"></a>

GET `/api/v1/records/{id}/`

#### Parameters

| Parameter name | Required | Type   | Description                                    |
| -------------- | -------- | ------ | ---------------------------------------------- |
| `id`           | ✅        | string | A UUID string identifying this Chatbot Record. |

#### Code examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X GET "https://api.maiagent.ai/api/v1/records/550e8400-e29b-41d4-a716-446655440000/" \
  -H "Authorization: Api-Key YOUR_API_KEY"

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY'
  }
};

axios.get("https://api.maiagent.ai/api/v1/records/550e8400-e29b-41d4-a716-446655440000/", config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request error:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/records/550e8400-e29b-41d4-a716-446655440000/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY"
}


response = requests.get(url, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request error:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->get("https://api.maiagent.ai/api/v1/records/550e8400-e29b-41d4-a716-446655440000/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request error: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response

**Status code: 200**

**Example response schema**

```typescript
{
  "id": string (uuid)
  "senderName": string // Returns the name of the user message sender
  "callerMemberId": string
  "callerMemberName": string
  "feedback": 
  {
    "id": string (uuid)
    "type": 
    {
    }
    "suggestion"?: string // Optional
    "updatedAt": string (timestamp)
  }
  "inputMessage": string
  "condenseMessage": string // User message rewritten from the chat history and context.
  "outputMessage": string
  "faithfulnessScore": number (double) // Whether the chatbot reply is grounded in the knowledge base rather than made up.
  "displayFaithfulnessScore": integer
  "answerRelevancyScore": number (double) // Whether the chatbot reply is relevant to the user's question.
  "displayAnswerRelevancyScore": integer
  "contextPrecisionScore": number (double) // Whether the chatbot reply is relevant to the reference context.
  "displayContextPrecisionScore": integer
  "answerCorrectnessScore": number (double) // Whether the chatbot reply is correct (requires a ground-truth answer).
  "displayAnswerCorrectnessScore": integer
  "answerSimilarityScore": number (double) // Whether the chatbot reply is similar to the ground-truth answer.
  "displayAnswerSimilarityScore": integer
  "contextRecallScore": number (double) // Whether the chatbot reply recalls the relevant information from the reference context.
  "displayContextRecallScore": integer
  "replyTime": string
  "createdAt": string (timestamp)
  "error": string // Error message raised during execution.
  "errorType": // May have different types
  string (enum: system, llm, embedding, reranker, vector_db, workflow, tool, timeout, client_interrupt, hook_blocked, evaluation, other) // Error source: system, LLM, embedding, reranker, and so on.

* `system` - System
* `llm` - LLM
* `embedding` - Embedding
* `reranker` - Reranker
* `vector_db` - Vector DB
* `workflow` - Workflow
* `tool` - Tool
* `timeout` - Timeout
* `client_interrupt` - Client Interrupt
* `hook_blocked` - Hook Blocked
* `evaluation` - Evaluation
* `other` - Other
  "processingTime": object // Returns timing statistics for each processing stage, including the streaming_metrics waterfall.
  "usage": 
  {
    "words":  // Word counts
    {
      "input": integer // Word count of the user message (including image counts converted to equivalent words)
      "output": integer // Word count of the AI assistant reply (including image counts converted to equivalent words)
      "total": integer // Sum of input and output
    }
    "tokens":  // token usage
    {
      "llm":  // LLM token usage
      {
        "input": integer // Number of LLM input tokens
        "output": integer // Number of LLM output tokens
        "cacheCreationInputTokens": integer // Number of input tokens written to the prompt cache
        "cacheReadInputTokens": integer // Number of input tokens read from the prompt cache
        "total": integer // Sum of input, output, and both cache token counts
      }
      "embedding": integer // Number of text embedding tokens
      "embeddingImage": integer // Number of image embedding tokens
      "reranker": integer // Number of chunks sent to the reranker
    }
    "llmCallBreakdown": [ // Token breakdown for each LLM call, sorted by call time; always an empty array on list endpoints. Use the detail endpoint instead.
      {
        "sequence": integer // LLM call sequence within this reply, starting at 1
        "inputTokens": integer // Number of input tokens for this call
        "outputTokens": integer // Number of output tokens for this call
        "cacheCreationInputTokens": integer // Number of tokens written to the prompt cache for this call
        "cacheReadInputTokens": integer // Number of tokens read from the prompt cache for this call
        "processingTimeSecs": number (double) // Duration of this call in seconds; null if not recorded
        "ttftSecs"?: number (double) // Time to first token for this call in seconds; null if not recorded. Some older records do not include this key. (Optional)
      }
    ]
  }
  "citationNodes": [
    {
      "id": string
      "text": string
      "chatbotFileId": string
      "fileName": string
      "url": string
    }
  ]
  "inputHookLogs": [
    {
      "id": string (uuid)
      "hook": string (uuid)
      "hookName": string
      "hookTypeLabel": string
      "hookAction": string
      "conversation": string (uuid)
      "message": string (uuid)
      "messageContent": string // Return the raw message text.
      "chatbotId": string
      "chatbotName": string
      "metadata": object
      "triggerPoint": string
      "createdAt": string (timestamp)
      "updatedAt": string (timestamp)
    }
  ]
  "outputHookLogs": [
    {
      "id": string (uuid)
      "hook": string (uuid)
      "hookName": string
      "hookTypeLabel": string
      "hookAction": string
      "conversation": string (uuid)
      "message": string (uuid)
      "messageContent": string // Return the raw message text.
      "chatbotId": string
      "chatbotName": string
      "metadata": object
      "triggerPoint": string
      "createdAt": string (timestamp)
      "updatedAt": string (timestamp)
    }
  ]
  "instructionId": string
  "llm": 
  {
    "id": string (uuid)
    "name": string
  }
  "effectiveMaxLlmOutputTokens": integer // max_tokens actually used for this reply, from Chatbot.custom_max_llm_output_tokens or LLM.max_tokens.
  "chatbot": 
  {
    "id": string (uuid)
    "name": string
  }
  "context": string
  "conversationId": string (uuid)
  "inboxId": string (uuid)
  "botMessageId": string (uuid)
  "userMessageId": string (uuid)
  "creditCosts": [ // Credit deductions for this reply, grouped by billing item. Null if the organization does not use credit-based billing, or if it does but this reply has no deduction transactions.
    {
      "itemCode": string // Billing item code
      "itemName": string // Billing item name
      "category": string // Billing item category
      "credits": number (double) // Total credits deducted for this billing item in this reply
      "quantity": number (double) // Total usage for this billing item (the unit depends on the item, such as token count)
      "unitPrice": number (double) // Unit price snapshot at the time of deduction; null if the unit price was not recorded
      "count": integer // Number of deduction transactions aggregated into this row
    }
  ]
  "cacheSavedCredits": number (double) // Net credits saved by prompt caching for this reply; ``None`` outside credit mode.

Reuses the usage-statistics saving semantics (``compute_cache_saved_credits``)
on the reply's own billing batch, so the per-reply figure and the hourly
aggregate are defined by the same formula. The net value may be negative
(cache-creation premium exceeding read savings); the frontend hides
non-positive values by design.
  "trace": object // Return the round trace timeline for the detail view; ``None`` for legacy records.
  "llmRawRequest": object // Raw messages array sent to the LLM on the final round, including system prompt and memory
  "source": string // Origin discriminator for the unified /records/ response.
  "schedule": object // Shape parity with scheduled rows, which link their owning ChatbotSchedule here.
  "voiceOrigin": object // The voice session and turn that delegated this question here, if it was a delegation.
}
```

**Example Response Values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "senderName": "Response string",
  "callerMemberId": "Response string",
  "callerMemberName": "Response string",
  "feedback": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": {},
    "suggestion": "Response string",
    "updatedAt": "Response string"
  },
  "inputMessage": "Response string",
  "condenseMessage": "Response string",
  "outputMessage": "Response string",
  "faithfulnessScore": 456,
  "displayFaithfulnessScore": 456,
  "answerRelevancyScore": 456,
  "displayAnswerRelevancyScore": 456,
  "contextPrecisionScore": 456,
  "displayContextPrecisionScore": 456,
  "answerCorrectnessScore": 456,
  "displayAnswerCorrectnessScore": 456,
  "answerSimilarityScore": 456,
  "displayAnswerSimilarityScore": 456,
  "contextRecallScore": 456,
  "displayContextRecallScore": 456,
  "replyTime": "Response string",
  "createdAt": "Response string",
  "error": "Response string",
  "errorType": "system",
  "processingTime": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "usage": {
    "words": {
      "input": 456,
      "output": 456,
      "total": 456
    },
    "tokens": {
      "llm": {
        "input": 456,
        "output": 456,
        "cacheCreationInputTokens": 456,
        "cacheReadInputTokens": 456,
        "total": 456
      },
      "embedding": 456,
      "embeddingImage": 456,
      "reranker": 456
    },
    "llmCallBreakdown": [
      {
        "sequence": 456,
        "inputTokens": 456,
        "outputTokens": 456,
        "cacheCreationInputTokens": 456,
        "cacheReadInputTokens": 456,
        "processingTimeSecs": 456,
        "ttftSecs": 456
      }
    ]
  },
  "citationNodes": [
    {
      "id": "Response string",
      "text": "Response string",
      "chatbotFileId": "Response string",
      "fileName": "Response string",
      "url": "Response string"
    }
  ],
  "inputHookLogs": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "hook": "550e8400-e29b-41d4-a716-446655440000",
      "hookName": "Response string",
      "hookTypeLabel": "Response string",
      "hookAction": "Response string",
      "conversation": "550e8400-e29b-41d4-a716-446655440000",
      "message": "550e8400-e29b-41d4-a716-446655440000",
      "messageContent": "Response string",
      "chatbotId": "Response string",
      "chatbotName": "Response string",
      "metadata": null,
      "triggerPoint": "Response string",
      "createdAt": "Response string",
      "updatedAt": "Response string"
    }
  ],
  "outputHookLogs": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "hook": "550e8400-e29b-41d4-a716-446655440000",
      "hookName": "Response string",
      "hookTypeLabel": "Response string",
      "hookAction": "Response string",
      "conversation": "550e8400-e29b-41d4-a716-446655440000",
      "message": "550e8400-e29b-41d4-a716-446655440000",
      "messageContent": "Response string",
      "chatbotId": "Response string",
      "chatbotName": "Response string",
      "metadata": null,
      "triggerPoint": "Response string",
      "createdAt": "Response string",
      "updatedAt": "Response string"
    }
  ],
  "instructionId": "Response string",
  "llm": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string"
  },
  "effectiveMaxLlmOutputTokens": 456,
  "chatbot": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string"
  },
  "context": "Response string",
  "conversationId": "550e8400-e29b-41d4-a716-446655440000",
  "inboxId": "550e8400-e29b-41d4-a716-446655440000",
  "botMessageId": "550e8400-e29b-41d4-a716-446655440000",
  "userMessageId": "550e8400-e29b-41d4-a716-446655440000",
  "creditCosts": [
    {
      "itemCode": "Response string",
      "itemName": "Response string",
      "category": "Response string",
      "credits": 456,
      "quantity": 456,
      "unitPrice": 456,
      "count": 456
    }
  ],
  "cacheSavedCredits": 456,
  "trace": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "llmRawRequest": null,
  "source": "Response string",
  "schedule": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "voiceOrigin": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  }
}
```

**Status Code: 404 - The specified conversation record was not found**

***

### Create a Conversation Record Export Request <a href="#records-export-excel-requests-create" id="records-export-excel-requests-create"></a>

POST `/api/v1/records/export-excel-requests/`

#### Request Body

**Request Parameters**

| Field              | Type               | Required | Description                                                                                                                                                                                                                                                                                                                      |
| ------------------ | ------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| chatbot            | string             | No       | Specify the Chatbot UUIDs to export. Multiple UUIDs can be separated by commas. Leave blank to export all Chatbots you have permission to access in the organization.                                                                                                                                                            |
| chatbotId          | string (uuid)      | No       | Specify the Chatbot UUID to export (an alias for chatbot; supports a single UUID only).                                                                                                                                                                                                                                          |
| keyword            | string             | No       | Filter conversation records by keyword (searches user messages and bot replies).                                                                                                                                                                                                                                                 |
| largeLanguageModel | string             | No       | Specify the large language model UUIDs to export. Multiple UUIDs can be separated by commas.                                                                                                                                                                                                                                     |
| startDate          | string (date)      | No       | Start date (YYYY-MM-DD), inclusive.                                                                                                                                                                                                                                                                                              |
| endDate            | string (date)      | No       | End date (YYYY-MM-DD), inclusive.                                                                                                                                                                                                                                                                                                |
| startDatetime      | string (timestamp) | No       | Exact start time (ISO 8601, inclusive), with the same semantics as the list drill-down filter.                                                                                                                                                                                                                                   |
| endDatetime        | string (timestamp) | No       | Exact end time (ISO 8601, exclusive), with the same semantics as the list drill-down filter.                                                                                                                                                                                                                                     |
| hasError           | boolean            | No       | Filter records by whether they have errors (true includes only records with errors; false includes only records without errors).                                                                                                                                                                                                 |
| errorType          | string             | No       | Filter by error type. Supports multiple comma-separated values (OR logic), using the same allowed values as the conversation record list.                                                                                                                                                                                        |
| hasFeedback        | boolean            | No       | Filter by whether user feedback exists (true includes only records with likes/dislikes; false includes only records without feedback).                                                                                                                                                                                           |
| feedbackType       | string             | No       | Filter by user feedback type (like / dislike). Supports multiple comma-separated values (OR logic).                                                                                                                                                                                                                              |
| callerMember       | string             | No       | Filter by the speaking member UUID. Supports multiple comma-separated values (OR logic).                                                                                                                                                                                                                                         |
| tool               | string (uuid)      | No       | Filter question-and-answer pairs that called this tool (tool UUID, including platform built-in tools; failed executions also count).                                                                                                                                                                                             |
| excludeBuiltIn     | boolean            | No       | When true, excludes data from official built-in Agents (locked instances created by renting from the Agent Marketplace), keeping only assistants created by the organization. Copies made with “Copy as custom” count as organization-created assistants. Omitting this parameter or setting it to false applies no such filter. |

**Example Request Schema**

```typescript
{
  "chatbot"?: string // Specify the Chatbot UUIDs to export. Multiple UUIDs can be separated by commas. Leave blank to export all Chatbots you have permission to access in the organization. (Optional)
  "chatbotId"?: string (uuid) // Specify the Chatbot UUID to export (an alias for chatbot; supports a single UUID only). (Optional)
  "keyword"?: string // Filter conversation records by keyword (searches user messages and bot replies). (Optional)
  "largeLanguageModel"?: string // Specify the large language model UUIDs to export. Multiple UUIDs can be separated by commas. (Optional)
  "startDate"?: string (date) // Start date (YYYY-MM-DD), inclusive. (Optional)
  "endDate"?: string (date) // End date (YYYY-MM-DD), inclusive. (Optional)
  "startDatetime"?: string (timestamp) // Exact start time (ISO 8601, inclusive), with the same semantics as the list drill-down filter. (Optional)
  "endDatetime"?: string (timestamp) // Exact end time (ISO 8601, exclusive), with the same semantics as the list drill-down filter. (Optional)
  "hasError"?: boolean // Filter records by whether they have errors (true includes only records with errors; false includes only records without errors). (Optional)
  "errorType"?: string // Filter by error type. Supports multiple comma-separated values (OR logic), using the same allowed values as the conversation record list. (Optional)
  "hasFeedback"?: boolean // Filter by whether user feedback exists (true includes only records with likes/dislikes; false includes only records without feedback). (Optional)
  "feedbackType"?: string // Filter by user feedback type (like / dislike). Supports multiple comma-separated values (OR logic). (Optional)
  "callerMember"?: string // Filter by the speaking member UUID. Supports multiple comma-separated values (OR logic). (Optional)
  "tool"?: string (uuid) // Filter question-and-answer pairs that called this tool (tool UUID, including platform built-in tools; failed executions also count). (Optional)
  "excludeBuiltIn"?: boolean // When true, excludes data from official built-in Agents (locked instances created by renting from the Agent Marketplace), keeping only assistants created by the organization. Copies made with “Copy as custom” count as organization-created assistants. Omitting this parameter or setting it to false applies no such filter. (Optional)
}
```

**Example Request Values**

```json
{
  "chatbot": "Example string",
  "chatbotId": "550e8400-e29b-41d4-a716-446655440000",
  "keyword": "Example string",
  "largeLanguageModel": "Example string",
  "startDate": "Example string",
  "endDate": "Example string",
  "startDatetime": "Example string",
  "endDatetime": "Example string",
  "hasError": true,
  "errorType": "Example string",
  "hasFeedback": true,
  "feedbackType": "Example string",
  "callerMember": "Example string",
  "tool": "550e8400-e29b-41d4-a716-446655440000",
  "excludeBuiltIn": true
}
```

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X POST "https://api.maiagent.ai/api/v1/records/export-excel-requests/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chatbot": "Example string",
    "chatbotId": "550e8400-e29b-41d4-a716-446655440000",
    "keyword": "Example string",
    "largeLanguageModel": "Example string",
    "startDate": "Example string",
    "endDate": "Example string",
    "startDatetime": "Example string",
    "endDatetime": "Example string",
    "hasError": true,
    "errorType": "Example string",
    "hasFeedback": true,
    "feedbackType": "Example string",
    "callerMember": "Example string",
    "tool": "550e8400-e29b-41d4-a716-446655440000",
    "excludeBuiltIn": true
  }'

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
};

// Request Body (payload)
const data = {
    "chatbot": "Example string",
    "chatbotId": "550e8400-e29b-41d4-a716-446655440000",
    "keyword": "Example string",
    "largeLanguageModel": "Example string",
    "startDate": "Example string",
    "endDate": "Example string",
    "startDatetime": "Example string",
    "endDatetime": "Example string",
    "hasError": true,
    "errorType": "Example string",
    "hasFeedback": true,
    "feedbackType": "Example string",
    "callerMember": "Example string",
    "tool": "550e8400-e29b-41d4-a716-446655440000",
    "excludeBuiltIn": true
  };

axios.post("https://api.maiagent.ai/api/v1/records/export-excel-requests/", data, config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request failed:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/records/export-excel-requests/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request Body (payload)
data = {
    "chatbot": "Example string",
    "chatbotId": "550e8400-e29b-41d4-a716-446655440000",
    "keyword": "Example string",
    "largeLanguageModel": "Example string",
    "startDate": "Example string",
    "endDate": "Example string",
    "startDatetime": "Example string",
    "endDatetime": "Example string",
    "hasError": True,
    "errorType": "Example string",
    "hasFeedback": True,
    "feedbackType": "Example string",
    "callerMember": "Example string",
    "tool": "550e8400-e29b-41d4-a716-446655440000",
    "excludeBuiltIn": True
}

response = requests.post(url, json=data, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request failed:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->post("https://api.maiagent.ai/api/v1/records/export-excel-requests/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'chatbot' => 'Example string',
            'chatbotId' => '550e8400-e29b-41d4-a716-446655440000',
            'keyword' => 'Example string',
            'largeLanguageModel' => 'Example string',
            'startDate' => 'Example string',
            'endDate' => 'Example string',
            'startDatetime' => 'Example string',
            'endDatetime' => 'Example string',
            'hasError' => true,
            'errorType' => 'Example string',
            'hasFeedback' => true,
            'feedbackType' => 'Example string',
            'callerMember' => 'Example string',
            'tool' => '550e8400-e29b-41d4-a716-446655440000',
            'excludeBuiltIn' => true
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request failed: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Body

**Status Code: 201**

**Example Response Schema**

```typescript
{
  "id": string (uuid)
  "userEmail": string (email)
  "recordType":  // Which monitoring list this export was requested from. Text exports read filter_params with the text record filters, voice exports with the voice session filters.

* `text` - Text records
* `voice` - Voice sessions
  {
  }
  "chatbotId": string (uuid) // Specify the Chatbot to export (deprecated; use chatbot_ids)
  "chatbotIds": object // List of Chatbot UUIDs to export; leave blank to export all Chatbots you have permission to access in the organization
  "startDate": string (date) // Start date for exported records (inclusive)
  "endDate": string (date) // End date for exported records (inclusive)
  "status": 
  {
  }
  "statusDisplay": string
  "fileUrl": string // Returns the file URL (valid permanently)
  "fileSize": integer // File size (bytes)
  "recordCount": integer // Total number of exported records
  "errorMessage": string
  "processingDurationSeconds": number (double) // Calculates the processing duration in seconds
  "createdAt": string (timestamp)
  "startedAt": string (timestamp)
  "completedAt": string (timestamp)
}
```

**Example Response Values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "userEmail": "response@example.com",
  "recordType": {},
  "chatbotId": "550e8400-e29b-41d4-a716-446655440000",
  "chatbotIds": null,
  "startDate": "Response string",
  "endDate": "Response string",
  "status": {},
  "statusDisplay": "Response string",
  "fileUrl": "Response string",
  "fileSize": 456,
  "recordCount": 456,
  "errorMessage": "Response string",
  "processingDurationSeconds": 456,
  "createdAt": "Response string",
  "startedAt": "Response string",
  "completedAt": "Response string"
}
```

***

### Get Conversation Record Export Status <a href="#records-export-excel-requests-retrieve2" id="records-export-excel-requests-retrieve2"></a>

GET `/api/v1/records/export-excel-requests/{exportId}/`

#### Parameters

| Parameter Name | Required | Type   | Description |
| -------------- | -------- | ------ | ----------- |
| `exportId`     | ✅        | string |             |

#### Code Examples

{% tabs %}
{% tab title="Shell/Bash" %}

```bash
# API call example (Shell)
curl -X GET "https://api.maiagent.ai/api/v1/records/export-excel-requests/{exportId}/" \
  -H "Authorization: Api-Key YOUR_API_KEY"

# Replace YOUR_API_KEY and verify the request data before running.
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const axios = require('axios');

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY'
  }
};

axios.get("https://api.maiagent.ai/api/v1/records/export-excel-requests/{exportId}/", config)
  .then(response => {
    console.log('Response received successfully:');
    console.log(response.data);
  })
  .catch(error => {
    console.error('Request failed:');
    console.error(error.response?.data || error.message);
  });
```

{% endtab %}

{% tab title="Python" %}

```python
import requests

url = "https://api.maiagent.ai/api/v1/records/export-excel-requests/{exportId}/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY"
}


response = requests.get(url, headers=headers)
try:
    print("Response received successfully:")
    print(response.json())
except Exception as e:
    print("Request failed:", e)
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();

try {
    $response = $client->get("https://api.maiagent.ai/api/v1/records/export-excel-requests/{exportId}/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY'
        ]
    ]);
    
    $data = json_decode($response->getBody(), true);
    echo "Response received successfully:\n";
    print_r($data);
} catch (Exception $e) {
    echo 'Request failed: ' . $e->getMessage();
}
?>
```

{% endtab %}
{% endtabs %}

#### Response Content

**Status Code: 200**

**Example Response Structure**

```typescript
{
  "id": string (uuid)
  "senderName": string // Returns the name of the user who sent the message
  "callerMemberId": string
  "callerMemberName": string
  "inputMessage": string
  "outputMessage": string
  "chatbot": 
  {
    "id": string (uuid)
    "name": string
  }
  "condenseMessage": string // User message rewritten from the chat history and context.
  "feedback": 
  {
    "id": string (uuid)
    "type": 
    {
    }
    "suggestion"?: string // Optional
    "updatedAt": string (timestamp)
  }
  "displayFaithfulnessScore": integer
  "displayAnswerRelevancyScore": integer
  "displayContextPrecisionScore": integer
  "displayAnswerCorrectnessScore": integer
  "displayAnswerSimilarityScore": integer
  "displayContextRecallScore": integer
  "replyTime": string
  "processingTime": object // Returns processing time statistics for the list view, using the same calculation as the detail view but excluding the streaming_metrics waterfall
  "usage": 
  {
    "words":  // Word counts
    {
      "input": integer // User message word count (including the word equivalent calculated from the number of images)
      "output": integer // AI assistant response word count (including the word equivalent calculated from the number of images)
      "total": integer // Sum of input and output
    }
    "tokens":  // token usage
    {
      "llm":  // LLM token usage
      {
        "input": integer // LLM input token count
        "output": integer // LLM output token count
        "cacheCreationInputTokens": integer // Input token count for prompt cache writes
        "cacheReadInputTokens": integer // Input token count for prompt cache reads
        "total": integer // Sum of input, output, and both cache token counts
      }
      "embedding": integer // Text embedding token count
      "embeddingImage": integer // Image embedding token count
      "reranker": integer // Number of chunks sent to the reranker
    }
    "llmCallBreakdown": [ // Token breakdown for each LLM call, ordered by call time; always an empty array on list endpoints. Use the detail endpoint instead
      {
        "sequence": integer // LLM call sequence number within this response, starting at 1
        "inputTokens": integer // Input token count for this call
        "outputTokens": integer // Output token count for this call
        "cacheCreationInputTokens": integer // Token count written to the prompt cache by this call
        "cacheReadInputTokens": integer // Token count read from the prompt cache by this call
        "processingTimeSecs": number (double) // Call duration in seconds; null if not recorded
        "ttftSecs"?: number (double) // Time to first token for this call in seconds; null if not recorded. Some older records omit this key (Optional)
      }
    ]
  }
  "llm": 
  {
    "id": string (uuid)
    "name": string
  }
  "createdAt": string (timestamp)
  "conversationId": string (uuid)
  "errorType": // May have different types
  string (enum: system, llm, embedding, reranker, vector_db, workflow, tool, timeout, client_interrupt, hook_blocked, evaluation, other) // Error source: system, LLM, embedding, reranker, and so on.

* `system` - System
* `llm` - LLM
* `embedding` - Embedding
* `reranker` - Reranker
* `vector_db` - Vector DB
* `workflow` - Workflow
* `tool` - Tool
* `timeout` - Timeout
* `client_interrupt` - Client Interrupt
* `hook_blocked` - Hook Blocked
* `evaluation` - Evaluation
* `other` - Other
  "creditCosts": [ // Credits deducted for this response, aggregated by billing item. Null if the organization does not use credit-based billing, or if it does but this response has no deduction transactions
    {
      "itemCode": string // Billing item code
      "itemName": string // Billing item name
      "category": string // Billing item category
      "credits": number (double) // Total credits deducted for this response under this billing item
      "quantity": number (double) // Total usage for this billing item (the unit depends on the item, such as token count)
      "unitPrice": number (double) // Unit price snapshot at the time of deduction; null if no unit price was recorded
      "count": integer // Number of deduction transactions aggregated into this row
    }
  ]
  "source": string // Origin discriminator for the unified /records/ response.
  "schedule": object // Shape parity with scheduled rows, which link their owning ChatbotSchedule here.
}
```

**Example Response Values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "senderName": "Response string",
  "callerMemberId": "Response string",
  "callerMemberName": "Response string",
  "inputMessage": "Response string",
  "outputMessage": "Response string",
  "chatbot": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string"
  },
  "condenseMessage": "Response string",
  "feedback": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "type": {},
    "suggestion": "Response string",
    "updatedAt": "Response string"
  },
  "displayFaithfulnessScore": 456,
  "displayAnswerRelevancyScore": 456,
  "displayContextPrecisionScore": 456,
  "displayAnswerCorrectnessScore": 456,
  "displayAnswerSimilarityScore": 456,
  "displayContextRecallScore": 456,
  "replyTime": "Response string",
  "processingTime": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "usage": {
    "words": {
      "input": 456,
      "output": 456,
      "total": 456
    },
    "tokens": {
      "llm": {
        "input": 456,
        "output": 456,
        "cacheCreationInputTokens": 456,
        "cacheReadInputTokens": 456,
        "total": 456
      },
      "embedding": 456,
      "embeddingImage": 456,
      "reranker": 456
    },
    "llmCallBreakdown": [
      {
        "sequence": 456,
        "inputTokens": 456,
        "outputTokens": 456,
        "cacheCreationInputTokens": 456,
        "cacheReadInputTokens": 456,
        "processingTimeSecs": 456,
        "ttftSecs": 456
      }
    ]
  },
  "llm": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string"
  },
  "createdAt": "Response string",
  "conversationId": "550e8400-e29b-41d4-a716-446655440000",
  "errorType": "system",
  "creditCosts": [
    {
      "itemCode": "Response string",
      "itemName": "Response string",
      "category": "Response string",
      "credits": 456,
      "quantity": 456,
      "unitPrice": 456,
      "count": 456
    }
  ],
  "source": "Response string",
  "schedule": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  }
}
```

***


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.maiagent.ai/api/en/api-reference/dui-hua-he-xun-xi.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
