> 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/lian-luo-ren.md).

# Contacts

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

GET `/api/v1/contacts/`

#### Parameters

| Parameter name | Required | Type    | Description                                                      |
| -------------- | -------- | ------- | ---------------------------------------------------------------- |
| `channels`     | ❌        | array   | Multiple values may be separated by commas.                      |
| `externalId`   | ❌        | string  | External user ID (exact match)                                   |
| `inbox`        | ❌        | string  | Deprecated: Use inboxes instead                                  |
| `inboxes`      | ❌        | string  | Conversation platform ID (filters contacts for a specific inbox) |
| `page`         | ❌        | integer | A page number within the paginated result set.                   |
| `pageSize`     | ❌        | integer | Number of results to return per page.                            |
| `query`        | ❌        | string  | Search by name or source ID                                      |
| `search`       | ❌        | string  | Full-text search                                                 |
| `tags`         | ❌        | array   | Multiple values may be separated by commas.                      |

#### Code examples

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

```bash
# API call example (Shell)
curl -X GET "https://api.maiagent.ai/api/v1/contacts/?channels=example&externalId=example&inbox=550e8400-e29b-41d4-a716-446655440000&inboxes=example&page=1&pageSize=1&query=example&search=example&tags=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/contacts/?channels=example&externalId=example&inbox=550e8400-e29b-41d4-a716-446655440000&inboxes=example&page=1&pageSize=1&query=example&search=example&tags=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/contacts/?channels=example&externalId=example&inbox=550e8400-e29b-41d4-a716-446655440000&inboxes=example&page=1&pageSize=1&query=example&search=example&tags=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/contacts/?channels=example&externalId=example&inbox=550e8400-e29b-41d4-a716-446655440000&inboxes=example&page=1&pageSize=1&query=example&search=example&tags=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)
      "inboxes"?: [ // Optional
        {
          "id": string (uuid)
          "name": string
        }
      ]
      "inbox"?:  // Backward-compatible field: a single inbox; ignored if inboxes is also provided (Optional)
      {
        "id": string (uuid)
        "name": string
      }
      "name": string
      "avatar"?: // May have different types (Optional)
      string (uri) // Contact avatar URL; supports an absolute URL or a relative path (Optional)
      "phoneNumber"?: string // Optional
      "email"?: // May have different types (Optional)
      string (email) // Optional
      "sourceId"?: string // Optional
      "externalId"?: string // External user ID: the user ID in the calling system, unique within the organization (excluding deleted contacts). Matching uses the exact original string and is case-sensitive; the conversation API uses message.externalUser.id to find the contact by this ID. Empty or whitespace-only strings return 400; pass null to clear the value. For multiple external systems, use a system prefix, such as projects:usr_123 (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 access to all knowledge bases, while omission imposes no restrictions (Optional)
          {
            "knowledgeBaseId": string (uuid) // Knowledge base ID
            "chatbotFileIds"?: [ // List of file IDs to exclude or select exclusively, depending on hasUserSelectedAll (Optional)
              string (uuid)
            ]
            "knowledgeBaseFileIds"?: [ // List of file IDs (the new name for chatbotFileIds; this field takes precedence if both are provided) (Optional)
              string (uuid)
            ]
            "faqIds"?: [ // List of FAQ IDs to exclude or select exclusively, 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 (it does not mean access is denied). Must be provided together with knowledgeBases; when provided alone, label filtering is not applied to knowledge base retrieval (equivalent to no restrictions) (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 // Custom customer information shown in the customer panel of the conversation page (Optional)
      "mcpCredentials": [ // List of MCP authentication credentials for the contact
        object
      ]
      "apiCredentials": [ // List of API authentication credentials for the contact
        object
      ]
      "contactConnectors": [ // List of Connector OAuth authorizations for the contact (excluding token values)
        object
      ]
      "tags": [
        {
          "id": string (uuid)
          "name": string
          "createdAt": string (timestamp)
          "updatedAt": string (timestamp)
        }
      ]
      "preChatFieldIndex": object // Pre-chat form field configuration from the inboxes this Contact belongs to, mapped as {field_id: {label: {locale: text}}}; the frontend uses it to resolve UUID keys in metadata to display names
      "createdAt": string (timestamp)
      "updatedAt": string (timestamp)
    }
  ]
}
```

**Example response value**

```json
{
  "count": 123,
  "next": "http://api.example.org/accounts/?page=4",
  "previous": "http://api.example.org/accounts/?page=2",
  "results": [
    [
      {
        "id": "user1_uuid",
        "inboxes": [
          {
            "id": "inbox_uuid",
            "name": "inbox_name"
          }
        ],
        "name": "user1",
        "avatar": "",
        "sourceId": null,
        "queryMetadata": null,
        "createdAt": "1751875361000",
        "updatedAt": "1751875361000"
      },
      {
        "id": "user2_uuid",
        "inboxes": [
          {
            "id": "inbox_uuid",
            "name": "inbox_name"
          }
        ],
        "name": "user2",
        "avatar": "avatar_url",
        "sourceId": "self defined source id",
        "queryMetadata": {
          "knowledgeBases": [
            {
              "knowledgeBaseId": "5f8c6a99-5515-43aa-8e83-fdeea327a3a2",
              "hasUserSelectedAll": true
            }
          ],
          "labelRelations": {
            "operator": "OR",
            "conditions": [
              {
                "labelId": "957998f9-1824-4872-becd-b7f8a7962896"
              }
            ]
          }
        },
        "createdAt": "1751875400000",
        "updatedAt": "1751875400000"
      }
    ]
  ]
}
```

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

***

### Create a contact <a href="#contacts-create" id="contacts-create"></a>

POST `/api/v1/contacts/`

#### Code examples

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

```bash
# API call example (Shell)
curl -X POST "https://api.maiagent.ai/api/v1/contacts/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inboxes": [
      {
        "id": "inbox_uuid"
      }
    ],
    "name": "user1",
    "avatar": "",
    "phoneNumber": 912345678,
    "email": "user1@example.com",
    "sourceId": "self defined source id",
    "queryMetadata": {
      "knowledgeBases": [
        {
          "knowledgeBaseId": "5f8c6a99-5515-43aa-8e83-fdeea327a3a2",
          "hasUserSelectedAll": true
        }
      ],
      "labelRelations": {
        "operator": "OR",
        "conditions": [
          {
            "labelId": "957998f9-1824-4872-becd-b7f8a7962896"
          }
        ]
      }
    },
    "metadata": [
      {
        "key": "Customer tier",
        "value": "VIP"
      },
      {
        "key": "Preferred language",
        "value": "Traditional Chinese"
      }
    ]
  }'

# 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 = {
    "inboxes": [
      {
        "id": "inbox_uuid"
      }
    ],
    "name": "user1",
    "avatar": "",
    "phoneNumber": 912345678,
    "email": "user1@example.com",
    "sourceId": "self defined source id",
    "queryMetadata": {
      "knowledgeBases": [
        {
          "knowledgeBaseId": "5f8c6a99-5515-43aa-8e83-fdeea327a3a2",
          "hasUserSelectedAll": true
        }
      ],
      "labelRelations": {
        "operator": "OR",
        "conditions": [
          {
            "labelId": "957998f9-1824-4872-becd-b7f8a7962896"
          }
        ]
      }
    },
    "metadata": [
      {
        "key": "Customer tier",
        "value": "VIP"
      },
      {
        "key": "Preferred language",
        "value": "Traditional Chinese"
      }
    ]
  };

axios.post("https://api.maiagent.ai/api/v1/contacts/", 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/contacts/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request body (payload)
data = {
    "inboxes": [
        {
            "id": "inbox_uuid"
        }
    ],
    "name": "user1",
    "avatar": "",
    "phoneNumber": 912345678,
    "email": "user1@example.com",
    "sourceId": "self defined source id",
    "queryMetadata": {
        "knowledgeBases": [
            {
                "knowledgeBaseId": "5f8c6a99-5515-43aa-8e83-fdeea327a3a2",
                "hasUserSelectedAll": True
            }
        ],
        "labelRelations": {
            "operator": "OR",
            "conditions": [
                {
                    "labelId": "957998f9-1824-4872-becd-b7f8a7962896"
                }
            ]
        }
    },
    "metadata": [
        {
            "key": "Customer tier",
            "value": "VIP"
        },
        {
            "key": "Preferred language",
            "value": "Traditional Chinese"
        }
    ]
}

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/contacts/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'inboxes' => [
                [
                    'id' => 'inbox_uuid'
                ]
            ],
            'name' => 'user1',
            'avatar' => '',
            'phoneNumber' => 912345678,
            'email' => 'user1@example.com',
            'sourceId' => 'self defined source id',
            'queryMetadata' => [
                'knowledgeBases' => [
                    [
                        'knowledgeBaseId' => '5f8c6a99-5515-43aa-8e83-fdeea327a3a2',
                        'hasUserSelectedAll' => true
                    ]
                ],
                'labelRelations' => [
                    'operator' => 'OR',
                    'conditions' => [
                        [
                            'labelId' => '957998f9-1824-4872-becd-b7f8a7962896'
                        ]
                    ]
                ]
            ],
            'metadata' => [
                [
                    'key' => 'Customer tier',
                    'value' => 'VIP'
                ],
                [
                    'key' => 'Preferred language',
                    'value' => 'Traditional Chinese'
                ]
            ]
        ]
    ]);
    
    $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: 201**

**Example response schema**

```typescript
{
  "id": string (uuid)
  "inboxes"?: [ // Optional
    {
      "id": string (uuid)
      "name": string
    }
  ]
  "inbox"?:  // Backward-compatible field: a single inbox; ignored if inboxes is also provided (Optional)
  {
    "id": string (uuid)
    "name": string
  }
  "name": string
  "avatar"?: // May have different types (Optional)
  string (uri) // Contact avatar URL; supports an absolute URL or a relative path (Optional)
  "phoneNumber"?: string // Optional
  "email"?: // May have different types (Optional)
  string (email) // Optional
  "sourceId"?: string // Optional
  "externalId"?: string // External user ID: the user ID in the calling system, unique within the organization (excluding deleted contacts). Matching uses the exact original string and is case-sensitive; the conversation API uses message.externalUser.id to find the contact by this ID. Empty or whitespace-only strings return 400; pass null to clear the value. For multiple external systems, use a system prefix, such as projects:usr_123 (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 access to all knowledge bases, while omission imposes no restrictions (Optional)
      {
        "knowledgeBaseId": string (uuid) // Knowledge base ID
        "chatbotFileIds"?: [ // List of file IDs to exclude or select exclusively, depending on hasUserSelectedAll (Optional)
          string (uuid)
        ]
        "knowledgeBaseFileIds"?: [ // List of file IDs (the new name for chatbotFileIds; this field takes precedence if both are provided) (Optional)
          string (uuid)
        ]
        "faqIds"?: [ // List of FAQ IDs to exclude or select exclusively, 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 (it does not mean access is denied). Must be provided together with knowledgeBases; when provided alone, label filtering is not applied to knowledge base retrieval (equivalent to no restrictions) (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 // Custom customer information shown in the customer panel of the conversation page (Optional)
  "mcpCredentials": [ // List of MCP authentication credentials for the contact
    object
  ]
  "apiCredentials": [ // List of API authentication credentials for the contact
    object
  ]
  "contactConnectors": [ // List of Connector OAuth authorizations for the contact (excluding token values)
    object
  ]
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "createdAt": string (timestamp)
      "updatedAt": string (timestamp)
    }
  ]
  "preChatFieldIndex": object // Pre-chat form field configuration from the inboxes this Contact belongs to, mapped as {field_id: {label: {locale: text}}}; the frontend uses it to resolve UUID keys in metadata to display names
  "createdAt": string (timestamp)
  "updatedAt": string (timestamp)
}
```

**Example response value**

```json
{
  "id": "user1_uuid",
  "inboxes": [
    {
      "id": "inbox_uuid",
      "name": "inbox_name"
    }
  ],
  "name": "user1",
  "avatar": "",
  "phoneNumber": 912345678,
  "email": "user1@example.com",
  "sourceId": null,
  "queryMetadata": null,
  "createdAt": "1751875361000",
  "updatedAt": "1751875361000"
}
```

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

***

### Get contact details <a href="#contacts-retrieve" id="contacts-retrieve"></a>

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

#### Parameters

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

#### Code examples

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

```bash
# API call example (Shell)
curl -X GET "https://api.maiagent.ai/api/v1/contacts/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/contacts/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/contacts/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/contacts/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)
  "inboxes"?: [ // Optional
    {
      "id": string (uuid)
      "name": string
    }
  ]
  "inbox"?:  // Backward-compatible field: a single inbox; ignored when inboxes is also provided (Optional)
  {
    "id": string (uuid)
    "name": string
  }
  "name": string
  "avatar"?: // May have different types (Optional)
  string (uri) // Contact avatar URL; supports absolute URLs or relative paths (Optional)
  "phoneNumber"?: string // Optional
  "email"?: // May have different types (Optional)
  string (email) // Optional
  "sourceId"?: string // Optional
  "externalId"?: string // External user ID: the user ID in the calling system, unique within the organization (excluding deleted contacts). Matching is exact and case-sensitive; the conversation API uses message.externalUser.id to find the contact by this ID. An empty or whitespace-only string returns 400; pass null to clear it. For multiple external systems, use a system prefix, such as projects:usr_123 (Optional)
  "queryMetadata"?: { // Query metadata controlling the scope of searchable knowledge; see each endpoint’s description (Optional)
  {
    "knowledgeBases"?: [ // List of queryable knowledge bases; an empty array or null denies all access, while omission leaves access 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"?: { // Tag filter criteria; an empty conditions array disables tag filtering (it does not deny access). Must be provided together with knowledgeBases. When provided 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; supports nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "metadata"?: object // Custom customer information shown in the customer panel of the conversation page (Optional)
  "mcpCredentials": [ // List of the contact’s MCP authentication credentials
    object
  ]
  "apiCredentials": [ // List of the contact’s API authentication credentials
    object
  ]
  "contactConnectors": [ // List of the contact’s Connector OAuth authorizations (excluding the tokens themselves)
    object
  ]
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "createdAt": string (timestamp)
      "updatedAt": string (timestamp)
    }
  ]
  "preChatFieldIndex": object // Pre-chat form field settings from the inboxes this Contact belongs to, mapped as {field_id: {label: {locale: text}}}. The frontend uses this mapping to resolve UUID keys in metadata to display names
  "createdAt": string (timestamp)
  "updatedAt": string (timestamp)
}
```

**Example Response Values**

```json
{
  "id": "user1_uuid",
  "inboxes": [
    {
      "id": "inbox_uuid",
      "name": "inbox_name"
    }
  ],
  "name": "user1",
  "avatar": "",
  "phoneNumber": 912345678,
  "email": "user1@example.com",
  "sourceId": null,
  "queryMetadata": null,
  "createdAt": "1751875361000",
  "updatedAt": "1751875361000"
}
```

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

***

### Update Contact <a href="#contacts-update" id="contacts-update"></a>

PUT `/api/v1/contacts/{id}/`

#### Parameters

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

#### Request Body

**Request Parameters**

| Field                                   | Type                   | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                            |
| --------------------------------------- | ---------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                                    | string                 | No       | Contact name                                                                                                                                                                                                                                                                                                                                                                                                           |
| inboxes                                 | array\[object]         | No       | List of conversation platforms (new format)                                                                                                                                                                                                                                                                                                                                                                            |
| inbox                                   | object                 | No       | Single conversation platform (legacy format, backward compatible)                                                                                                                                                                                                                                                                                                                                                      |
| inbox.id                                | string (uuid)          | Yes      | Conversation platform ID                                                                                                                                                                                                                                                                                                                                                                                               |
| avatar                                  | string                 | No       | Avatar URL                                                                                                                                                                                                                                                                                                                                                                                                             |
| phoneNumber                             | string                 | No       | Contact phone number                                                                                                                                                                                                                                                                                                                                                                                                   |
| email                                   | string (email)         | No       | Contact email address                                                                                                                                                                                                                                                                                                                                                                                                  |
| sourceId                                | string                 | No       | Source ID                                                                                                                                                                                                                                                                                                                                                                                                              |
| queryMetadata                           | object                 | No       | Query Metadata controls the scope of searchable knowledge. Omitted or null means unset (unrestricted); an empty object {} also means unrestricted (all selected), rather than no access. If knowledgeBases is an empty array or null, no knowledge can be queried (no access). An empty labelRelations.conditions array disables tag filtering (it does not deny access). See this endpoint’s description for details. |
| queryMetadata.knowledgeBases            | array\[object]         | No       | List of queryable knowledge bases; an empty array or null denies all access, while omission leaves access unrestricted                                                                                                                                                                                                                                                                                                 |
| queryMetadata.labelRelations            | object                 | No       | Tag filter criteria; an empty conditions array disables tag filtering (it does not deny access). Must be provided together with knowledgeBases. When provided alone, tag filtering is not applied to knowledge base retrieval (equivalent to unrestricted access)                                                                                                                                                      |
| queryMetadata.labelRelations.operator   | string (enum: AND, OR) | Yes      | How tag conditions are combined                                                                                                                                                                                                                                                                                                                                                                                        |
| queryMetadata.labelRelations.conditions | array\[any]            | No       | List of tag conditions; supports nested operator/conditions combinations                                                                                                                                                                                                                                                                                                                                               |
| metadata                                | array\[object]         | No       | List of custom contact attributes                                                                                                                                                                                                                                                                                                                                                                                      |

**Request Schema Example**

```typescript
{
  "name"?: string // Contact name (Optional)
  "inboxes"?: [ // List of conversation platforms (new format) (Optional)
    {
      "id": string (uuid) // Conversation platform ID
    }
  ]
  "inbox"?: { // Single conversation platform (legacy format, backward compatible) (Optional)
  {
    "id": string (uuid) // Conversation platform ID
  }
  }
  "avatar"?: string // Avatar URL (Optional)
  "phoneNumber"?: string // Contact phone number (Optional)
  "email"?: string (email) // Contact email address (Optional)
  "sourceId"?: string // Source ID (Optional)
  "queryMetadata"?: { // Query Metadata controls the scope of searchable knowledge. Omitted or null means unset (unrestricted); an empty object {} also means unrestricted (all selected), rather than no access. If knowledgeBases is an empty array or null, no knowledge can be queried (no access). An empty labelRelations.conditions array disables tag filtering (it does not deny access). See this endpoint’s description for details. (Optional)
  {
    "knowledgeBases"?: [ // List of queryable knowledge bases; an empty array or null denies all access, while omission leaves access 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"?: { // Tag filter criteria; an empty conditions array disables tag filtering (it does not deny access). Must be provided together with knowledgeBases. When provided 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; supports nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "metadata"?: [ // List of custom contact attributes (Optional)
    {
      "key": string // Attribute name
      "value": string // Attribute value
      "updatedAt"?: string // Update time (ISO format; optional, populated automatically if omitted) (Optional)
    }
  ]
}
```

**Example Request Values**

```json
{
  "inboxes": [
    {
      "id": "new_inbox_uuid"
    }
  ],
  "name": "updated_user_name",
  "avatar": "updated_avatar_url",
  "phoneNumber": 987654321,
  "email": "updated@example.com",
  "sourceId": "updated_source_id",
  "queryMetadata": {
    "knowledgeBases": []
  },
  "metadata": [
    {
      "key": "Customer tier",
      "value": "VVIP"
    },
    {
      "key": "Preferred language",
      "value": "日本語"
    }
  ]
}
```

#### Code Examples

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

```bash
# API call example (Shell)
curl -X PUT "https://api.maiagent.ai/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inboxes": [
      {
        "id": "new_inbox_uuid"
      }
    ],
    "name": "updated_user_name",
    "avatar": "updated_avatar_url",
    "phoneNumber": 987654321,
    "email": "updated@example.com",
    "sourceId": "updated_source_id",
    "queryMetadata": {
      "knowledgeBases": []
    },
    "metadata": [
      {
        "key": "Customer tier",
        "value": "VVIP"
      },
      {
        "key": "Preferred language",
        "value": "日本語"
      }
    ]
  }'

# 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 = {
    "inboxes": [
      {
        "id": "new_inbox_uuid"
      }
    ],
    "name": "updated_user_name",
    "avatar": "updated_avatar_url",
    "phoneNumber": 987654321,
    "email": "updated@example.com",
    "sourceId": "updated_source_id",
    "queryMetadata": {
      "knowledgeBases": []
    },
    "metadata": [
      {
        "key": "Customer tier",
        "value": "VVIP"
      },
      {
        "key": "Preferred language",
        "value": "日本語"
      }
    ]
  };

axios.put("https://api.maiagent.ai/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000/", 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/contacts/550e8400-e29b-41d4-a716-446655440000/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request body (payload)
data = {
    "inboxes": [
        {
            "id": "new_inbox_uuid"
        }
    ],
    "name": "updated_user_name",
    "avatar": "updated_avatar_url",
    "phoneNumber": 987654321,
    "email": "updated@example.com",
    "sourceId": "updated_source_id",
    "queryMetadata": {
        "knowledgeBases": []
    },
    "metadata": [
        {
            "key": "Customer tier",
            "value": "VVIP"
        },
        {
            "key": "Preferred language",
            "value": "日本語"
        }
    ]
}

response = requests.put(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->put("https://api.maiagent.ai/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'inboxes' => [
                [
                    'id' => 'new_inbox_uuid'
                ]
            ],
            'name' => 'updated_user_name',
            'avatar' => 'updated_avatar_url',
            'phoneNumber' => 987654321,
            'email' => 'updated@example.com',
            'sourceId' => 'updated_source_id',
            'queryMetadata' => [
                'knowledgeBases' => []
            ],
            'metadata' => [
                [
                    'key' => 'Customer tier',
                    'value' => 'VVIP'
                ],
                [
                    'key' => 'Preferred language',
                    'value' => '日本語'
                ]
            ]
        ]
    ]);
    
    $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)
  "inboxes"?: [ // Optional
    {
      "id": string (uuid)
      "name": string
    }
  ]
  "inbox"?:  // Backward-compatible field: a single inbox; ignored when inboxes is also provided (Optional)
  {
    "id": string (uuid)
    "name": string
  }
  "name": string
  "avatar"?: // May have different types (Optional)
  string (uri) // Contact avatar URL; supports absolute URLs or relative paths (Optional)
  "phoneNumber"?: string // Optional
  "email"?: // May have different types (Optional)
  string (email) // Optional
  "sourceId"?: string // Optional
  "externalId"?: string // External user ID: the user ID in the calling system, unique within the organization (excluding deleted contacts). Matching is exact and case-sensitive; the conversation API uses message.externalUser.id to find the contact by this ID. An empty or whitespace-only string returns 400; pass null to clear it. For multiple external systems, use a system prefix, such as projects:usr_123 (Optional)
  "queryMetadata"?: { // Query metadata controlling the scope of searchable knowledge; see each endpoint’s description (Optional)
  {
    "knowledgeBases"?: [ // List of queryable knowledge bases; an empty array or null denies all access, while omission leaves access 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"?: { // Tag filter criteria; an empty conditions array disables tag filtering (it does not deny access). Must be provided together with knowledgeBases. When provided 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; supports nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "metadata"?: object // Custom customer information shown in the customer panel of the conversation page (Optional)
  "mcpCredentials": [ // List of the contact’s MCP authentication credentials
    object
  ]
  "apiCredentials": [ // List of the contact’s API authentication credentials
    object
  ]
  "contactConnectors": [ // List of the contact’s Connector OAuth authorizations (excluding the tokens themselves)
    object
  ]
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "createdAt": string (timestamp)
      "updatedAt": string (timestamp)
    }
  ]
  "preChatFieldIndex": object // Pre-chat form field settings from the inboxes this Contact belongs to, mapped as {field_id: {label: {locale: text}}}. The frontend uses this mapping to resolve UUID keys in metadata to display names
  "createdAt": string (timestamp)
  "updatedAt": string (timestamp)
}
```

**Example Response Values**

```json
{
  "id": "user1_uuid",
  "inboxes": [
    {
      "id": "inbox_uuid",
      "name": "inbox_name"
    }
  ],
  "name": "user1",
  "avatar": "",
  "phoneNumber": 912345678,
  "email": "user1@example.com",
  "sourceId": null,
  "queryMetadata": null,
  "createdAt": "1751875361000",
  "updatedAt": "1751875361000"
}
```

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

***

### Partially Update Contact <a href="#contacts-partial-update" id="contacts-partial-update"></a>

PATCH `/api/v1/contacts/{id}/`

#### Parameters

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

#### Request Body

**Request Parameters**

| Field                                   | Type                   | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                            |
| --------------------------------------- | ---------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name                                    | string                 | No       | Contact name                                                                                                                                                                                                                                                                                                                                                                                                           |
| inboxes                                 | array\[object]         | No       | List of conversation platforms (new format)                                                                                                                                                                                                                                                                                                                                                                            |
| inbox                                   | object                 | No       | Single conversation platform (legacy format, backward compatible)                                                                                                                                                                                                                                                                                                                                                      |
| inbox.id                                | string (uuid)          | Yes      | Conversation platform ID                                                                                                                                                                                                                                                                                                                                                                                               |
| avatar                                  | string                 | No       | Avatar URL                                                                                                                                                                                                                                                                                                                                                                                                             |
| phoneNumber                             | string                 | No       | Contact phone number                                                                                                                                                                                                                                                                                                                                                                                                   |
| email                                   | string (email)         | No       | Contact email address                                                                                                                                                                                                                                                                                                                                                                                                  |
| sourceId                                | string                 | No       | Source ID                                                                                                                                                                                                                                                                                                                                                                                                              |
| queryMetadata                           | object                 | No       | Query Metadata controls the scope of searchable knowledge. Omitted or null means unset (unrestricted); an empty object {} also means unrestricted (all selected), rather than no access. If knowledgeBases is an empty array or null, no knowledge can be queried (no access). An empty labelRelations.conditions array disables tag filtering (it does not deny access). See this endpoint’s description for details. |
| queryMetadata.knowledgeBases            | array\[object]         | No       | List of queryable knowledge bases; an empty array or null denies all access, while omission leaves access unrestricted                                                                                                                                                                                                                                                                                                 |
| queryMetadata.labelRelations            | object                 | No       | Tag filter criteria; an empty conditions array disables tag filtering (it does not deny access). Must be provided together with knowledgeBases. When provided alone, tag filtering is not applied to knowledge base retrieval (equivalent to unrestricted access)                                                                                                                                                      |
| queryMetadata.labelRelations.operator   | string (enum: AND, OR) | Yes      | How tag conditions are combined                                                                                                                                                                                                                                                                                                                                                                                        |
| queryMetadata.labelRelations.conditions | array\[any]            | No       | List of tag conditions; supports nested operator/conditions combinations                                                                                                                                                                                                                                                                                                                                               |
| metadata                                | array\[object]         | No       | List of custom contact attributes                                                                                                                                                                                                                                                                                                                                                                                      |

**Request Schema Example**

```typescript
{
  "name"?: string // Contact name (Optional)
  "inboxes"?: [ // List of conversation platforms (new format) (Optional)
    {
      "id": string (uuid) // Conversation platform ID
    }
  ]
  "inbox"?: { // Single conversation platform (legacy format, backward compatible) (Optional)
  {
    "id": string (uuid) // Conversation platform ID
  }
  }
  "avatar"?: string // Avatar URL (Optional)
  "phoneNumber"?: string // Contact phone number (Optional)
  "email"?: string (email) // Contact email address (Optional)
  "sourceId"?: string // Source ID (Optional)
  "queryMetadata"?: { // Query Metadata controls the scope of searchable knowledge. Omitted or null means unset (unrestricted); an empty object {} also means unrestricted (all selected), rather than no access. If knowledgeBases is an empty array or null, no knowledge can be queried (no access). An empty labelRelations.conditions array disables tag filtering (it does not deny access). See this endpoint’s description for details. (Optional)
  {
    "knowledgeBases"?: [ // List of queryable knowledge bases; an empty array or null denies all access, while omission leaves access 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"?: { // Tag filter criteria; an empty conditions array disables tag filtering (it does not deny access). Must be provided together with knowledgeBases. When provided 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; supports nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "metadata"?: [ // List of custom contact attributes (Optional)
    {
      "key": string // Attribute name
      "value": string // Attribute value
      "updatedAt"?: string // Update time (ISO format; optional, populated automatically if omitted) (Optional)
    }
  ]
}
```

**Example Request Values**

```json
{
  "inboxes": [
    {
      "id": "new_inbox_uuid"
    }
  ],
  "name": "updated_user_name",
  "avatar": "updated_avatar_url",
  "phoneNumber": 987654321,
  "email": "updated@example.com",
  "sourceId": "updated_source_id",
  "queryMetadata": {
    "knowledgeBases": []
  },
  "metadata": [
    {
      "key": "Customer tier",
      "value": "VVIP"
    },
    {
      "key": "Preferred language",
      "value": "日本語"
    }
  ]
}
```

#### Code Examples

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

```bash
# API call example (Shell)
curl -X PATCH "https://api.maiagent.ai/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inboxes": [
      {
        "id": "new_inbox_uuid"
      }
    ],
    "name": "updated_user_name",
    "avatar": "updated_avatar_url",
    "phoneNumber": 987654321,
    "email": "updated@example.com",
    "sourceId": "updated_source_id",
    "queryMetadata": {
      "knowledgeBases": []
    },
    "metadata": [
      {
        "key": "Customer tier",
        "value": "VVIP"
      },
      {
        "key": "Preferred language",
        "value": "日本語"
      }
    ]
  }'

# 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 = {
    "inboxes": [
      {
        "id": "new_inbox_uuid"
      }
    ],
    "name": "updated_user_name",
    "avatar": "updated_avatar_url",
    "phoneNumber": 987654321,
    "email": "updated@example.com",
    "sourceId": "updated_source_id",
    "queryMetadata": {
      "knowledgeBases": []
    },
    "metadata": [
      {
        "key": "Customer tier",
        "value": "VVIP"
      },
      {
        "key": "Preferred language",
        "value": "Japanese"
      }
    ]
  };

axios.patch("https://api.maiagent.ai/api/v1/contacts/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/contacts/550e8400-e29b-41d4-a716-446655440000/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request body (payload)
data = {
    "inboxes": [
        {
            "id": "new_inbox_uuid"
        }
    ],
    "name": "updated_user_name",
    "avatar": "updated_avatar_url",
    "phoneNumber": 987654321,
    "email": "updated@example.com",
    "sourceId": "updated_source_id",
    "queryMetadata": {
        "knowledgeBases": []
    },
    "metadata": [
        {
            "key": "Customer tier",
            "value": "VVIP"
        },
        {
            "key": "Preferred language",
            "value": "Japanese"
        }
    ]
}

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/contacts/550e8400-e29b-41d4-a716-446655440000/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'inboxes' => [
                [
                    'id' => 'new_inbox_uuid'
                ]
            ],
            'name' => 'updated_user_name',
            'avatar' => 'updated_avatar_url',
            'phoneNumber' => 987654321,
            'email' => 'updated@example.com',
            'sourceId' => 'updated_source_id',
            'queryMetadata' => [
                'knowledgeBases' => []
            ],
            'metadata' => [
                [
                    'key' => 'Customer tier',
                    'value' => 'VVIP'
                ],
                [
                    'key' => 'Preferred language',
                    'value' => 'Japanese'
                ]
            ]
        ]
    ]);
    
    $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 structure**

```typescript
{
  "id": string (uuid)
  "inboxes"?: [ // Optional
    {
      "id": string (uuid)
      "name": string
    }
  ]
  "inbox"?:  // Backward compatibility field: a single inbox; ignored if inboxes is also provided (Optional)
  {
    "id": string (uuid)
    "name": string
  }
  "name": string
  "avatar"?: // May have different types (Optional)
  string (uri) // Contact avatar URL; supports absolute URLs or relative paths (Optional)
  "phoneNumber"?: string // Optional
  "email"?: // May have different types (Optional)
  string (email) // Optional
  "sourceId"?: string // Optional
  "externalId"?: string // External user ID: the user ID in the calling system, unique within the organization (excluding deleted contacts). Matching uses the exact, case-sensitive string; the conversation API uses message.externalUser.id to find the contact by this ID. Empty or whitespace-only strings return 400; pass null to clear it. For multiple external systems, use a system prefix, such as projects:usr_123 (Optional)
  "queryMetadata"?: { // Query metadata that controls the scope of accessible knowledge; see each endpoint for details (Optional)
  {
    "knowledgeBases"?: [ // List of accessible knowledge bases; an empty array or null denies access to all knowledge bases (no permission); 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 if 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 (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 // Custom customer information shown in the customer panel of the conversation page (Optional)
  "mcpCredentials": [ // List of MCP authentication credentials for the contact
    object
  ]
  "apiCredentials": [ // List of API authentication credentials for the contact
    object
  ]
  "contactConnectors": [ // List of Connector OAuth authorizations for the contact (excluding token contents)
    object
  ]
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "createdAt": string (timestamp)
      "updatedAt": string (timestamp)
    }
  ]
  "preChatFieldIndex": object // Pre-chat form field settings from the inboxes this Contact belongs to, mapped as {field_id: {label: {locale: text}}}; the frontend uses these settings to resolve UUID keys in metadata to display names
  "createdAt": string (timestamp)
  "updatedAt": string (timestamp)
}
```

**Example response values**

```json
{
  "id": "user1_uuid",
  "inboxes": [
    {
      "id": "inbox_uuid",
      "name": "inbox_name"
    }
  ],
  "name": "user1",
  "avatar": "",
  "phoneNumber": 912345678,
  "email": "user1@example.com",
  "sourceId": null,
  "queryMetadata": null,
  "createdAt": "1751875361000",
  "updatedAt": "1751875361000"
}
```

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

***

### Delete Contact <a href="#contacts-destroy" id="contacts-destroy"></a>

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

#### Parameters

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

#### Code Examples

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

```bash
# API call example (Shell)
curl -X DELETE "https://api.maiagent.ai/api/v1/contacts/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 payload
const data = null;

axios.delete("https://api.maiagent.ai/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000/", 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/contacts/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 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/contacts/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 | Description                                                          |
| ----------- | -------------------------------------------------------------------- |
| 204         | No response body                                                     |
| 400         | The contact still has associated conversations and cannot be deleted |

***

### List a Contact’s Conversations <a href="#contacts-conversations-list" id="contacts-conversations-list"></a>

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

#### Parameters

| Parameter Name | Required | Type    | Description                                                                                                  |
| -------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| `id`           | ✅        | string  | A UUID string identifying this Contact.                                                                      |
| `channels`     | ❌        | array   | Multiple values may be separated by commas.                                                                  |
| `externalId`   | ❌        | string  | External user ID (exact match)                                                                               |
| `inbox`        | ❌        | string  | Filter by a single inbox ID (UUID format)                                                                    |
| `inboxes`      | ❌        | string  | Filter by multiple inbox IDs (comma-separated, for example: uuid1,uuid2, or use multiple inboxes parameters) |
| `keyword`      | ❌        | string  | Search message content in conversations (case-insensitive)                                                   |
| `page`         | ❌        | integer | A page number within the paginated result set.                                                               |
| `pageSize`     | ❌        | integer | Number of results to return per page.                                                                        |
| `query`        | ❌        | string  |                                                                                                              |
| `tags`         | ❌        | array   | Multiple values may be separated by commas.                                                                  |

#### Code Examples

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

```bash
# API request example (Shell)
curl -X GET "https://api.maiagent.ai/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000/conversations/?channels=example&externalId=example&inbox=example&inboxes=example&keyword=example&page=1&pageSize=1&query=example&tags=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/contacts/550e8400-e29b-41d4-a716-446655440000/conversations/?channels=example&externalId=example&inbox=example&inboxes=example&keyword=example&page=1&pageSize=1&query=example&tags=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/contacts/550e8400-e29b-41d4-a716-446655440000/conversations/?channels=example&externalId=example&inbox=example&inboxes=example&keyword=example&page=1&pageSize=1&query=example&tags=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/contacts/550e8400-e29b-41d4-a716-446655440000/conversations/?channels=example&externalId=example&inbox=example&inboxes=example&keyword=example&page=1&pageSize=1&query=example&tags=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 Body

**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 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 that controls the scope of searchable knowledge; see each endpoint’s description for details (Optional)
      {
        "knowledgeBases"?: [ // List of searchable knowledge bases; an empty array or null means none are searchable (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"?: { // Tag filter criteria; an empty conditions array means no tag filtering (not a lack of permission). Must be provided together with knowledgeBases; when provided alone, tag filtering is not applied to knowledge base retrieval (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 this 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 means no thinking (fastest); model_default means the model thinks on its own or the platform’s default level enables thinking (Optional)
        "unsetLevel"?: string // Level actually sent by the platform when none is set; null means no thinking parameter is sent and the model’s own default applies (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) / idle (inactivity); null if not created by splitting
      "continuedFrom": object // Conversation continued by this split-created conversation: {id, last_message_at, deleted}; if the previous conversation has been deleted, id=null and deleted=true
      "continuedTo": object // Latest 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
{
  "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").**

***

### Get the Contact’s Latest Conversation <a href="#contacts-conversations-latest-retrieve" id="contacts-conversations-latest-retrieve"></a>

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

#### Parameters

| Parameter name | Required | Type   | Description                                                                                           |
| -------------- | -------- | ------ | ----------------------------------------------------------------------------------------------------- |
| `id`           | ✅        | string | A UUID string identifying this Contact.                                                               |
| `inbox`        | ❌        | string | Filter by a single inbox ID (UUID format)                                                             |
| `inboxes`      | ❌        | string | Filter by multiple inbox IDs (comma-separated, e.g., uuid1,uuid2, or use multiple inboxes parameters) |
| `keyword`      | ❌        | string | Search message content in conversations (case-insensitive)                                            |

#### Code Examples

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

```bash
# API call example (Shell)
curl -X GET "https://api.maiagent.ai/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000/conversations/latest/?inbox=example&inboxes=example&keyword=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/contacts/550e8400-e29b-41d4-a716-446655440000/conversations/latest/?inbox=example&inboxes=example&keyword=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/contacts/550e8400-e29b-41d4-a716-446655440000/conversations/latest/?inbox=example&inboxes=example&keyword=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/contacts/550e8400-e29b-41d4-a716-446655440000/conversations/latest/?inbox=example&inboxes=example&keyword=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
{
  "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;
the frontend conversation sidebar needs to display them, 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’s description (Optional)
  {
    "knowledgeBases"?: [ // List of searchable knowledge bases; an empty array or null means none are searchable (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 if 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 conditions; an empty conditions array means no tag filtering (not a lack of permission). Must be provided together with knowledgeBases; when provided 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) // Effective behavior when neither the conversation nor the assistant specifies a thinking level: off = no thinking (fastest); model_default = the model decides, or the platform’s default level enables thinking (Optional)
    "unsetLevel"?: string // Level actually sent by the platform when none is set; null means no thinking parameter is sent, leaving the model’s own 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 toggle; the continuation conversation includes source_conversation_id/source_title; the original handed-off conversation 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 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: 404 - No response body**

***

### Broadcast Messages <a href="#contacts-broadcast-message-create" id="contacts-broadcast-message-create"></a>

POST `/api/v1/contacts/broadcast-message/`

#### Request Body

**Request Parameters**

| Field             | Type           | Required | Description                                                                   |
| ----------------- | -------------- | -------- | ----------------------------------------------------------------------------- |
| contactIds        | array\[string] | No       | \[Explicit selection mode] List of contact IDs to send messages to            |
| inboxId           | string (uuid)  | No       | \[Exclusion/send-to-all mode] Inbox ID; sends to all contacts in this inbox   |
| excludeContactIds | array\[string] | No       | \[Exclusion mode] List of contact IDs to exclude; must be used with inbox\_id |
| message           | string         | Yes      | Message content to send                                                       |

**Example Request Schema**

```typescript
{
  "contactIds"?: [ // [Explicit selection mode] List of contact IDs to send messages to (Optional)
    string (uuid)
  ]
  "inboxId"?: string (uuid) // [Exclusion/send-to-all mode] Inbox ID; sends to all contacts in this inbox (Optional)
  "excludeContactIds"?: [ // [Exclusion mode] List of contact IDs to exclude; must be used with inbox_id (Optional)
    string (uuid)
  ]
  "message": string // Message content to send
}
```

**Example Request Values**

```json
{
  "contactIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "inboxId": "550e8400-e29b-41d4-a716-446655440000",
  "excludeContactIds": [
    "550e8400-e29b-41d4-a716-446655440000"
  ],
  "message": "This is a sample message"
}
```

#### Code Examples

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

```bash
# API call example (Shell)
curl -X POST "https://api.maiagent.ai/api/v1/contacts/broadcast-message/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contactIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ],
    "inboxId": "550e8400-e29b-41d4-a716-446655440000",
    "excludeContactIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ],
    "message": "This is a sample message"
  }'

# 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 = {
    "contactIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ],
    "inboxId": "550e8400-e29b-41d4-a716-446655440000",
    "excludeContactIds": [
      "550e8400-e29b-41d4-a716-446655440000"
    ],
    "message": "This is a sample message"
  };

axios.post("https://api.maiagent.ai/api/v1/contacts/broadcast-message/", 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/contacts/broadcast-message/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request Body (payload)
data = {
    "contactIds": [
        "550e8400-e29b-41d4-a716-446655440000"
    ],
    "inboxId": "550e8400-e29b-41d4-a716-446655440000",
    "excludeContactIds": [
        "550e8400-e29b-41d4-a716-446655440000"
    ],
    "message": "This is a sample message"
}

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/contacts/broadcast-message/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'contactIds' => [
                '550e8400-e29b-41d4-a716-446655440000'
            ],
            'inboxId' => '550e8400-e29b-41d4-a716-446655440000',
            'excludeContactIds' => [
                '550e8400-e29b-41d4-a716-446655440000'
            ],
            'message' => 'This is a sample message'
        ]
    ]);
    
    $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
{
  "results"?: [ // Optional
    {
      "contactId"?: string (uuid) // Optional
      "conversationId"?: string (uuid) // Optional
      "messageId"?: string (uuid) // Optional
      "status"?: string // Optional
    }
  ]
  "errors"?: [ // Optional
    {
      "contactId"?: string (uuid) // Optional
      "error"?: string // Optional
    }
  ]
  "totalContacts"?: integer // Optional
  "successCount"?: integer // Optional
  "errorCount"?: integer // Optional
}
```

**Example Response Values**

```json
{
  "results": [
    {
      "contactId": "550e8400-e29b-41d4-a716-446655440000",
      "conversationId": "550e8400-e29b-41d4-a716-446655440000",
      "messageId": "550e8400-e29b-41d4-a716-446655440000",
      "status": "success"
    }
  ],
  "errors": [
    {
      "contactId": "550e8400-e29b-41d4-a716-446655440000",
      "error": "Response string"
    }
  ],
  "totalContacts": 456,
  "successCount": 456,
  "errorCount": 456
}
```

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

***

### Create API Credentials for a Contact <a href="#contacts-api-credentials-create" id="contacts-api-credentials-create"></a>

POST `/api/v1/contacts/{id}/api-credentials/`

{% hint style="info" %}
Allow the Agent to call your API tools on behalf of this contact. For the workflow, see the technical manual: [Contact Credential Synchronization](https://docs.maiagent.ai/tech/authorization-integration/contact-credentials-sync)。
{% endhint %}

#### Parameters

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

#### Request Body

**Request Parameters**

| Field   | Type          | Required | Description                          |
| ------- | ------------- | -------- | ------------------------------------ |
| tool    | string (uuid) | Yes      | API tool ID                          |
| headers | object        | No       | Dictionary of authentication headers |

**Example Request Schema**

```typescript
{
  "tool": string (uuid) // API tool ID
  "headers"?: object // Dictionary of authentication headers (Optional)
}
```

**Example Request Values**

```json
{
  "tool": "550e8400-e29b-41d4-a716-446655440000",
  "headers": {}
}
```

#### Code Examples

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

```bash
# API call example (Shell)
curl -X POST "https://api.maiagent.ai/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000/api-credentials/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tool": "550e8400-e29b-41d4-a716-446655440000",
    "headers": {}
  }'

# 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 = {
    "tool": "550e8400-e29b-41d4-a716-446655440000",
    "headers": {}
  };

axios.post("https://api.maiagent.ai/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000/api-credentials/", 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/contacts/550e8400-e29b-41d4-a716-446655440000/api-credentials/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request Body (payload)
data = {
    "tool": "550e8400-e29b-41d4-a716-446655440000",
    "headers": {}
}

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/contacts/550e8400-e29b-41d4-a716-446655440000/api-credentials/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'tool' => '550e8400-e29b-41d4-a716-446655440000',
            'headers' => 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: 201**

**Example Response Schema**

```typescript
{
  "id": string (uuid)
  "inboxes"?: [ // Optional
    {
      "id": string (uuid)
      "name": string
    }
  ]
  "inbox"?:  // Backward-compatible field: a single inbox; ignored when inboxes is also provided (Optional)
  {
    "id": string (uuid)
    "name": string
  }
  "name": string
  "avatar"?: // May have different types (Optional)
  string (uri) // Contact avatar URL; supports absolute URLs or relative paths (Optional)
  "phoneNumber"?: string // Optional
  "email"?: // May have different types (Optional)
  string (email) // Optional
  "sourceId"?: string // Optional
  "externalId"?: string // External user ID: the user ID in the calling system, unique within the organization (excluding deleted contacts). Matching uses the exact original string and is case-sensitive; the conversation API uses message.externalUser.id to find the contact by this value. Empty or whitespace-only strings return 400; pass null to clear it. For multiple external systems, use a system prefix, such as projects:usr_123 (Optional)
  "queryMetadata"?: { // Query metadata that controls the scope of queryable knowledge; see each endpoint description for details (Optional)
  {
    "knowledgeBases"?: [ // List of queryable knowledge bases; an empty array or null denies all access (no permission); 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 if 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 access). 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 // Custom customer information shown in the customer panel of the conversation page (Optional)
  "mcpCredentials": [ // List of MCP authentication credentials for the contact
    object
  ]
  "apiCredentials": [ // List of API authentication credentials for the contact
    object
  ]
  "contactConnectors": [ // List of Connector OAuth authorizations for the contact (excluding the tokens themselves)
    object
  ]
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "createdAt": string (timestamp)
      "updatedAt": string (timestamp)
    }
  ]
  "preChatFieldIndex": object // Pre-chat form field settings from the inboxes this Contact belongs to, mapped as {field_id: {label: {locale: text}}}; the frontend uses this to resolve UUID keys in metadata to display names
  "createdAt": string (timestamp)
  "updatedAt": string (timestamp)
}
```

**Example Response Values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "inboxes": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string"
    }
  ],
  "inbox": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string"
  },
  "name": "Response string",
  "avatar": "https://example.com/file.jpg",
  "phoneNumber": "Response string",
  "email": "response@example.com",
  "sourceId": "Response string",
  "externalId": "Response string",
  "queryMetadata": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "metadata": null,
  "mcpCredentials": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "apiCredentials": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "contactConnectors": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "tags": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string",
      "createdAt": "Response string",
      "updatedAt": "Response string"
    }
  ],
  "preChatFieldIndex": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "createdAt": "Response string",
  "updatedAt": "Response string"
}
```

**Status Code: 400 - No response body**

***

### Update API Credentials for a Contact <a href="#contacts-api-credentials-partial-update" id="contacts-api-credentials-partial-update"></a>

PATCH `/api/v1/contacts/{id}/api-credentials/{credentialId}/`

#### Parameters

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

#### Request Body

**Request Parameters**

| Field   | Type   | Required | Description                          |
| ------- | ------ | -------- | ------------------------------------ |
| headers | object | No       | Dictionary of authentication headers |

**Example Request Schema**

```typescript
{
  "headers"?: object // Dictionary of authentication headers (Optional)
}
```

**Example Request Values**

```json
{
  "headers": {}
}
```

#### Code Examples

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

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

# 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 = {
    "headers": {}
  };

axios.patch("https://api.maiagent.ai/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000/api-credentials/{credentialId}/", 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/contacts/550e8400-e29b-41d4-a716-446655440000/api-credentials/{credentialId}/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

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

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/contacts/550e8400-e29b-41d4-a716-446655440000/api-credentials/{credentialId}/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'headers' => 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

**Status code: 200**

**Example response schema**

```typescript
{
  "id": string (uuid)
  "inboxes"?: [ // Optional
    {
      "id": string (uuid)
      "name": string
    }
  ]
  "inbox"?:  // Backward-compatible field: a single inbox; ignored if inboxes is also provided (Optional)
  {
    "id": string (uuid)
    "name": string
  }
  "name": string
  "avatar"?: // May have different types (Optional)
  string (uri) // Contact avatar URL; supports absolute URLs or relative paths (Optional)
  "phoneNumber"?: string // Optional
  "email"?: // May have different types (Optional)
  string (email) // Optional
  "sourceId"?: string // Optional
  "externalId"?: string // External user ID: the user ID in the calling system, unique within the organization (excluding deleted contacts). Matching uses the exact string and is case-sensitive; the conversation API uses message.externalUser.id to find the contact by this ID. Empty or whitespace-only strings return 400; pass null to clear it. When using multiple external systems, a system prefix is recommended, such as projects:usr_123 (Optional)
  "queryMetadata"?: { // Query metadata that controls 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); 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 = 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 together with knowledgeBases; when provided alone, tag filtering is not applied to knowledge base retrieval (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
      ]
    }
    }
  }
  }
  "metadata"?: object // Custom customer information shown in the customer panel of the conversation page (Optional)
  "mcpCredentials": [ // List of MCP credentials for the contact
    object
  ]
  "apiCredentials": [ // List of API credentials for the contact
    object
  ]
  "contactConnectors": [ // List of Connector OAuth authorizations for the contact (excluding the tokens themselves)
    object
  ]
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "createdAt": string (timestamp)
      "updatedAt": string (timestamp)
    }
  ]
  "preChatFieldIndex": object // Pre-chat form field configuration from the inboxes this Contact belongs to, with mapping {field_id: {label: {locale: text}}}; the frontend uses it to resolve UUID keys in metadata to display names
  "createdAt": string (timestamp)
  "updatedAt": string (timestamp)
}
```

**Example response values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "inboxes": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string"
    }
  ],
  "inbox": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string"
  },
  "name": "Response string",
  "avatar": "https://example.com/file.jpg",
  "phoneNumber": "Response string",
  "email": "response@example.com",
  "sourceId": "Response string",
  "externalId": "Response string",
  "queryMetadata": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "metadata": null,
  "mcpCredentials": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "apiCredentials": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "contactConnectors": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "tags": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string",
      "createdAt": "Response string",
      "updatedAt": "Response string"
    }
  ],
  "preChatFieldIndex": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "createdAt": "Response string",
  "updatedAt": "Response string"
}
```

**Status code: 404 - No response body**

***

### Delete Contact's API Credentials <a href="#contacts-api-credentials-destroy" id="contacts-api-credentials-destroy"></a>

DELETE `/api/v1/contacts/{id}/api-credentials/{credentialId}/`

#### Parameters

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

#### Code Examples

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

```bash
# API call example (Shell)
curl -X DELETE "https://api.maiagent.ai/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000/api-credentials/{credentialId}/" \
  -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/contacts/550e8400-e29b-41d4-a716-446655440000/api-credentials/{credentialId}/", 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/contacts/550e8400-e29b-41d4-a716-446655440000/api-credentials/{credentialId}/"
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 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/contacts/550e8400-e29b-41d4-a716-446655440000/api-credentials/{credentialId}/", [
        '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 | Description      |
| ----------- | ---------------- |
| 204         | No response body |
| 404         | No response body |

***

### Create MCP credentials for a contact <a href="#contacts-mcp-credentials-create" id="contacts-mcp-credentials-create"></a>

POST `/api/v1/contacts/{id}/mcp-credentials/`

#### Parameters

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

#### Request body

**Request parameters**

| Field   | Type          | Required | Description                          |
| ------- | ------------- | -------- | ------------------------------------ |
| tool    | string (uuid) | Yes      | MCP tool ID                          |
| headers | object        | No       | Dictionary of authentication headers |

**Example request schema**

```typescript
{
  "tool": string (uuid) // MCP tool ID
  "headers"?: object // Dictionary of authentication headers (Optional)
}
```

**Example request values**

```json
{
  "tool": "550e8400-e29b-41d4-a716-446655440000",
  "headers": {}
}
```

#### Code examples

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

```bash
# API call example (Shell)
curl -X POST "https://api.maiagent.ai/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000/mcp-credentials/" \
  -H "Authorization: Api-Key YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tool": "550e8400-e29b-41d4-a716-446655440000",
    "headers": {}
  }'

# 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 = {
    "tool": "550e8400-e29b-41d4-a716-446655440000",
    "headers": {}
  };

axios.post("https://api.maiagent.ai/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000/mcp-credentials/", 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/contacts/550e8400-e29b-41d4-a716-446655440000/mcp-credentials/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request body (payload)
data = {
    "tool": "550e8400-e29b-41d4-a716-446655440000",
    "headers": {}
}

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/contacts/550e8400-e29b-41d4-a716-446655440000/mcp-credentials/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'tool' => '550e8400-e29b-41d4-a716-446655440000',
            'headers' => 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: 201**

**Example response schema**

```typescript
{
  "id": string (uuid)
  "inboxes"?: [ // Optional
    {
      "id": string (uuid)
      "name": string
    }
  ]
  "inbox"?:  // Backward-compatible field: a single inbox; ignored if inboxes is also provided (Optional)
  {
    "id": string (uuid)
    "name": string
  }
  "name": string
  "avatar"?: // May have different types (Optional)
  string (uri) // Contact avatar URL; supports absolute URLs or relative paths (Optional)
  "phoneNumber"?: string // Optional
  "email"?: // May have different types (Optional)
  string (email) // Optional
  "sourceId"?: string // Optional
  "externalId"?: string // External user ID: the user ID in the calling system, unique within the organization (excluding deleted contacts). Matched exactly and case-sensitively against the original string; the conversation API uses message.externalUser.id to find the contact by this ID. Empty or whitespace-only strings return 400; pass null to clear it. When using multiple external systems, a system prefix is recommended, such as projects:usr_123 (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 means none can be searched (no permission); 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 if 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 content in the knowledge base except the listed items; false selects only the listed items (Optional)
      }
    ]
    "labelRelations"?: { // Tag filter criteria; an empty conditions array means no tag filtering (it does not mean no permission). Must be provided together with knowledgeBases; when provided alone, tag filtering is not applied to knowledge base retrieval (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
      ]
    }
    }
  }
  }
  "metadata"?: object // Custom customer information shown in the customer panel of the conversation page (Optional)
  "mcpCredentials": [ // List of MCP authentication credentials for the contact
    object
  ]
  "apiCredentials": [ // List of API authentication credentials for the contact
    object
  ]
  "contactConnectors": [ // List of Connector OAuth authorizations for the contact (excluding token values)
    object
  ]
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "createdAt": string (timestamp)
      "updatedAt": string (timestamp)
    }
  ]
  "preChatFieldIndex": object // Pre-chat form field settings from the inboxes this Contact belongs to, mapped as {field_id: {label: {locale: text}}}; the frontend uses these settings to resolve UUID keys in metadata to display names
  "createdAt": string (timestamp)
  "updatedAt": string (timestamp)
}
```

**Example response values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "inboxes": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string"
    }
  ],
  "inbox": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string"
  },
  "name": "Response string",
  "avatar": "https://example.com/file.jpg",
  "phoneNumber": "Response string",
  "email": "response@example.com",
  "sourceId": "Response string",
  "externalId": "Response string",
  "queryMetadata": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "metadata": null,
  "mcpCredentials": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "apiCredentials": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "contactConnectors": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "tags": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string",
      "createdAt": "Response string",
      "updatedAt": "Response string"
    }
  ],
  "preChatFieldIndex": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "createdAt": "Response string",
  "updatedAt": "Response string"
}
```

**Status code: 400 - No response body**

***

### Update MCP credentials for a contact <a href="#contacts-mcp-credentials-partial-update" id="contacts-mcp-credentials-partial-update"></a>

PATCH `/api/v1/contacts/{id}/mcp-credentials/{credentialId}/`

#### Parameters

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

#### Request body

**Request parameters**

| Field   | Type   | Required | Description                          |
| ------- | ------ | -------- | ------------------------------------ |
| headers | object | No       | Dictionary of authentication headers |

**Example request schema**

```typescript
{
  "headers"?: object // Dictionary of authentication headers (Optional)
}
```

**Example request values**

```json
{
  "headers": {}
}
```

#### Code examples

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

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

# 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 = {
    "headers": {}
  };

axios.patch("https://api.maiagent.ai/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000/mcp-credentials/{credentialId}/", 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/contacts/550e8400-e29b-41d4-a716-446655440000/mcp-credentials/{credentialId}/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Request body (payload)
data = {
    "headers": {}
}

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/contacts/550e8400-e29b-41d4-a716-446655440000/mcp-credentials/{credentialId}/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY',
            'Content-Type' => 'application/json'
        ],
        'json' => [
            'headers' => 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)
  "inboxes"?: [ // Optional
    {
      "id": string (uuid)
      "name": string
    }
  ]
  "inbox"?:  // Backward-compatible field: a single inbox; ignored if inboxes is also provided (Optional)
  {
    "id": string (uuid)
    "name": string
  }
  "name": string
  "avatar"?: // May have different types (Optional)
  string (uri) // Contact avatar URL; supports absolute URLs or relative paths (Optional)
  "phoneNumber"?: string // Optional
  "email"?: // May have different types (Optional)
  string (email) // Optional
  "sourceId"?: string // Optional
  "externalId"?: string // External user ID: the user ID in the calling system, unique within the organization (excluding deleted contacts). Matched exactly and case-sensitively against the original string; the conversation API uses message.externalUser.id to find the contact by this ID. Empty or whitespace-only strings return 400; pass null to clear it. When using multiple external systems, a system prefix is recommended, such as projects:usr_123 (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 means none can be searched (no permission); 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 if 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 content in the knowledge base except the listed items; false selects only the listed items (Optional)
      }
    ]
    "labelRelations"?: { // Tag filter criteria; an empty conditions array means no tag filtering (it does not mean no permission). Must be provided together with knowledgeBases; when provided alone, tag filtering is not applied to knowledge base retrieval (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
      ]
    }
    }
  }
  }
  "metadata"?: object // Custom customer information shown in the customer panel of the conversation page (Optional)
  "mcpCredentials": [ // List of MCP authentication credentials for the contact
    object
  ]
  "apiCredentials": [ // List of API authentication credentials for the contact
    object
  ]
  "contactConnectors": [ // List of Connector OAuth authorizations for the contact (excluding token values)
    object
  ]
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "createdAt": string (timestamp)
      "updatedAt": string (timestamp)
    }
  ]
  "preChatFieldIndex": object // Pre-chat form field settings from the inboxes this Contact belongs to, mapped as {field_id: {label: {locale: text}}}; the frontend uses these settings to resolve UUID keys in metadata to display names
  "createdAt": string (timestamp)
  "updatedAt": string (timestamp)
}
```

**Example response values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "inboxes": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string"
    }
  ],
  "inbox": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string"
  },
  "name": "Response string",
  "avatar": "https://example.com/file.jpg",
  "phoneNumber": "Response string",
  "email": "response@example.com",
  "sourceId": "Response string",
  "externalId": "Response string",
  "queryMetadata": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "metadata": null,
  "mcpCredentials": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "apiCredentials": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "contactConnectors": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "tags": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string",
      "createdAt": "Response string",
      "updatedAt": "Response string"
    }
  ],
  "preChatFieldIndex": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "createdAt": "Response string",
  "updatedAt": "Response string"
}
```

**Status code: 404 - No response body**

***

### Delete Contact's MCP Credentials <a href="#contacts-mcp-credentials-destroy" id="contacts-mcp-credentials-destroy"></a>

DELETE `/api/v1/contacts/{id}/mcp-credentials/{credentialId}/`

#### Parameters

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

#### Code Examples

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

```bash
# API call example (Shell)
curl -X DELETE "https://api.maiagent.ai/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000/mcp-credentials/{credentialId}/" \
  -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/contacts/550e8400-e29b-41d4-a716-446655440000/mcp-credentials/{credentialId}/", 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/contacts/550e8400-e29b-41d4-a716-446655440000/mcp-credentials/{credentialId}/"
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 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/contacts/550e8400-e29b-41d4-a716-446655440000/mcp-credentials/{credentialId}/", [
        '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 | Description      |
| ----------- | ---------------- |
| 204         | No response body |
| 404         | No response body |

***

### Download the Contact Bulk Import Template <a href="#contacts-bulk-import-template-retrieve" id="contacts-bulk-import-template-retrieve"></a>

GET `/api/v1/contacts/bulk-import-template/`

#### Code Examples

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

```bash
# API Call Example (Shell)
curl -X GET "https://api.maiagent.ai/api/v1/contacts/bulk-import-template/" \
  -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/contacts/bulk-import-template/", 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/contacts/bulk-import-template/"
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/contacts/bulk-import-template/", [
        '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)
  "inboxes"?: [ // Optional
    {
      "id": string (uuid)
      "name": string
    }
  ]
  "inbox"?:  // Backward-compatible field: a single inbox; ignored if inboxes is also provided (Optional)
  {
    "id": string (uuid)
    "name": string
  }
  "name": string
  "avatar"?: // May have different types (Optional)
  string (uri) // Contact avatar URL; supports an absolute URL or a relative path (Optional)
  "phoneNumber"?: string // Optional
  "email"?: // May have different types (Optional)
  string (email) // Optional
  "sourceId"?: string // Optional
  "externalId"?: string // External user ID: the user ID in the calling system, unique within the organization (excluding deleted contacts). Matching uses the exact, case-sensitive string; the conversation API uses message.externalUser.id to find the contact by this ID. An empty or whitespace-only string returns 400; pass null to clear the value. For multiple external systems, use a system prefix, such as projects:usr_123 (Optional)
  "queryMetadata"?: { // Query metadata that controls which knowledge can be queried; see each endpoint description (Optional)
  {
    "knowledgeBases"?: [ // List of queryable knowledge bases; an empty array or null means none can be queried (no access), while omitting the 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 if 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 means no tag filtering (not no access). Must be provided together 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; supports nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "metadata"?: object // Custom customer information shown in the customer panel of the conversation page (Optional)
  "mcpCredentials": [ // List of MCP authentication credentials for the contact
    object
  ]
  "apiCredentials": [ // List of API authentication credentials for the contact
    object
  ]
  "contactConnectors": [ // List of Connector OAuth authorizations for the contact (excluding the tokens themselves)
    object
  ]
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "createdAt": string (timestamp)
      "updatedAt": string (timestamp)
    }
  ]
  "preChatFieldIndex": object // Pre-chat form field settings from the inboxes this Contact belongs to, mapped as {field_id: {label: {locale: text}}}; the frontend uses these settings to resolve UUID keys in metadata to display names
  "createdAt": string (timestamp)
  "updatedAt": string (timestamp)
}
```

**Example Response Values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "inboxes": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string"
    }
  ],
  "inbox": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string"
  },
  "name": "Response string",
  "avatar": "https://example.com/file.jpg",
  "phoneNumber": "Response string",
  "email": "response@example.com",
  "sourceId": "Response string",
  "externalId": "Response string",
  "queryMetadata": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "metadata": null,
  "mcpCredentials": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "apiCredentials": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "contactConnectors": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "tags": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string",
      "createdAt": "Response string",
      "updatedAt": "Response string"
    }
  ],
  "preChatFieldIndex": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "createdAt": "Response string",
  "updatedAt": "Response string"
}
```

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

***

### Bulk Import Contacts <a href="#contacts-bulk-import-create" id="contacts-bulk-import-create"></a>

POST `/api/v1/contacts/bulk-import/`

{% hint style="info" %}
Upload an Excel file filled out using the template; pass `dry_run=true` to report only the number of records that would be created or updated without writing any data (returns `200` rather than `201`).
{% endhint %}

#### Code Examples

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

```bash
# API Call Example (Shell)
curl -X POST "https://api.maiagent.ai/api/v1/contacts/bulk-import/" \
  -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');
const FormData = require('form-data');

// Create a FormData object
const formData = new FormData();
// Add form fields

// Set request headers
const config = {
  headers: {
    'Authorization': 'Api-Key YOUR_API_KEY',
    ...formData.getHeaders()
  }
};

axios.post("https://api.maiagent.ai/api/v1/contacts/bulk-import/", formData, 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/contacts/bulk-import/"
headers = {
    "Authorization": "Api-Key YOUR_API_KEY"
}

# Create file and form data
files = {
    'file': ('example.pdf', open('example.pdf', 'rb'), 'application/pdf')
}

response = requests.post(url, files=files, 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/contacts/bulk-import/", [
        'headers' => [
            'Authorization' => 'Api-Key YOUR_API_KEY'
        ],
        'multipart' => [
            [
                'name' => 'file',
                'contents' => fopen('example.pdf', 'r'),
                'filename' => 'example.pdf'
            ]
        ]
    ]);
    
    $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)
  "inboxes"?: [ // Optional
    {
      "id": string (uuid)
      "name": string
    }
  ]
  "inbox"?:  // Backward-compatible field: a single inbox; ignored if inboxes is also provided (Optional)
  {
    "id": string (uuid)
    "name": string
  }
  "name": string
  "avatar"?: // May have different types (Optional)
  string (uri) // Contact avatar URL; supports an absolute URL or a relative path (Optional)
  "phoneNumber"?: string // Optional
  "email"?: // May have different types (Optional)
  string (email) // Optional
  "sourceId"?: string // Optional
  "externalId"?: string // External user ID: the user ID in the calling system, unique within the organization (excluding deleted contacts). Matching uses the exact, case-sensitive string; the conversation API uses message.externalUser.id to find the contact by this ID. An empty or whitespace-only string returns 400; pass null to clear the value. For multiple external systems, use a system prefix, such as projects:usr_123 (Optional)
  "queryMetadata"?: { // Query metadata that controls which knowledge can be queried; see each endpoint description (Optional)
  {
    "knowledgeBases"?: [ // List of queryable knowledge bases; an empty array or null means none can be queried (no access), while omitting the 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 if 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 means no tag filtering (not no access). Must be provided together 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; supports nested operator/conditions combinations (Optional)
        object
      ]
    }
    }
  }
  }
  "metadata"?: object // Custom customer information shown in the customer panel of the conversation page (Optional)
  "mcpCredentials": [ // List of MCP authentication credentials for the contact
    object
  ]
  "apiCredentials": [ // List of API authentication credentials for the contact
    object
  ]
  "contactConnectors": [ // List of Connector OAuth authorizations for the contact (excluding the tokens themselves)
    object
  ]
  "tags": [
    {
      "id": string (uuid)
      "name": string
      "createdAt": string (timestamp)
      "updatedAt": string (timestamp)
    }
  ]
  "preChatFieldIndex": object // Pre-chat form field settings from the inboxes this Contact belongs to, mapped as {field_id: {label: {locale: text}}}; the frontend uses these settings to resolve UUID keys in metadata to display names
  "createdAt": string (timestamp)
  "updatedAt": string (timestamp)
}
```

**Example Response Values**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "inboxes": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string"
    }
  ],
  "inbox": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Response string"
  },
  "name": "Response string",
  "avatar": "https://example.com/file.jpg",
  "phoneNumber": "Response string",
  "email": "response@example.com",
  "sourceId": "Response string",
  "externalId": "Response string",
  "queryMetadata": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "metadata": null,
  "mcpCredentials": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "apiCredentials": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "contactConnectors": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Example response name",
      "description": "Example response description"
    }
  ],
  "tags": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Response string",
      "createdAt": "Response string",
      "updatedAt": "Response string"
    }
  ],
  "preChatFieldIndex": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Example response name",
    "description": "Example response description"
  },
  "createdAt": "Response string",
  "updatedAt": "Response string"
}
```

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

***


---

# 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/lian-luo-ren.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.
