
# 消息与对话

Messages API 让您可以向任何联系人发送消息、读取对话、更正或删除已发送的消息、对消息做出反应、拉取完整的聊天会话线程、导出记录，以及将聊天标记为已读或未读——所有这些操作都无需打开收件箱。

本页面上的所有路径均相对于基础 URL `https://api.youraiconnector.com/v1`。每个请求都需要您的 API 密钥——请参阅[身份验证](authentication.md)以获取发送密钥的完整方式列表。以下示例使用了 `X-API-Key` 标头，其中一个 cURL 示例也展示了 `?apiKey=` 查询形式。

> **投递工作原理：** 发送消息**不会**等待其送达。API 会接收您的消息，立即返回一个消息 ID，然后在后台通过联系人的渠道（WhatsApp、短信、Instagram 等）进行投递。要跟踪消息是否实际送达或已读，请通过[网络钩子 (Webhooks)](webhooks.md) 监听状态更新——请勿使用轮询。发送响应仅确认消息已被接收。

---

## 发送消息

有两种发送方式。选择最适合您识别联系人方式的一种：

- **按联系人 ID 发送** — 您已经知道联系人的 ID（例如，您通过 API 创建了该联系人或从 Webhook 获取了该 ID）。使用 `POST /contacts/{contactId}/send-message`。
- **按联系人身份发送** — 您知道联系人的电话号码、Instagram ID 等，但不知道其内部 ID。使用 `POST /contacts/send` 并让平台找到正确的联系人。

两者都以相同的方式将消息加入队列，并根据联系人所在的渠道进行投递。您无需选择传输方式——平台会自动将 WhatsApp 联系人的消息通过 WhatsApp 发送，将短信联系人的消息通过短信发送，依此类推。

### 按联系人 ID 发送

`POST /contacts/{contactId}/send-message`

| 字段 | 必填 | 描述 |
|---|---|---|
| `body` | 是 | 要发送的消息文本。 |
| `mediaUrl` | 否 | 要附加的媒体文件（图片、文档等）的 URL。 |
| `mediaContentType` | 否 | 附加媒体的 MIME 类型，例如 `image/jpeg`。 |
| `pauseBot` | 否 | `true` 在发送消息时暂停该联系人的 AI——适用于人工接管的情况。请参阅[暂停或恢复 AI](#pause-or-resume-the-ai-for-one-contact)。 |
| `clearIncompleteReply` | 否 | `true` 会丢弃未完成的机器人回复，以免在您发送消息后自动恢复。 |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/send-message" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "Hi! Your appointment is confirmed for tomorrow at 10:00."
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/send-message",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      body: "Hi! Your appointment is confirmed for tomorrow at 10:00.",
    }),
  }
);
const data = await res.json();
console.log(data.messageId);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/send-message",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"body": "Hi! Your appointment is confirmed for tomorrow at 10:00."},
)
print(res.json()["messageId"])
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "messageId": "aB3dE5fG7hI9jK1lM2nO",
  "contactId": "contact123",
  "channel": "whatsapp",
  "message": "Message created successfully. Delivery is being processed."
}
```

### 按联系人身份发送

`POST /contacts/send`

当您没有联系人的内部 ID 时，请使用此方法。提供消息 `body` 以及 `contact_id`，**或者**提供 `channel` 以及与该渠道匹配的身份字段。

| 字段 | 必填 | 说明 |
|---|---|---|
| `body` | 是 | 要发送的消息文本。 |
| `contact_id` | 否 | 现有联系人的 ID。设置此项后，无需填写下方的身份字段。 |
| `channel` | 否 | 发送消息的渠道。当未提供 `contact_id` 时必填。14 个可外发渠道之一：`whatsapp`, `whatsapp_web`, `sms`, `instagram`, `instagram_private`, `messenger`, `telegram`, `chat-widget`, `custom`, `email`, `line`, `imessage`, `linkedin`, `viber`。 |
| `phone_number` | 否 | 联系人的国际格式电话号码。与 `whatsapp`, `whatsapp_web` 和 `sms` 配合使用。 |
| `instagram_id` | 否 | 联系人的 Instagram 用户 ID。与 `instagram` 配合使用。 |
| `messenger_id` | 否 | 联系人的 Messenger 用户 ID。与 `messenger` 配合使用。 |
| `telegram_user_id` | 否 | 联系人的 Telegram 用户 ID。与 `telegram` 配合使用。 |
| `media_url` | 否 | 要附加的媒体文件 URL。 |
| `media_content_type` | 否 | 附加媒体的 MIME 类型，例如 `image/jpeg`。 |

**哪些渠道可以通过身份信息解析。** 14 个渠道中只有 6 个接受身份字段而非 `contact_id`：`whatsapp`, `whatsapp_web` 和 `sms` 通过 `phone_number` 查找，`instagram` 通过 `instagram_id` 查找，`messenger` 通过 `messenger_id` 查找，`telegram` 通过 `telegram_user_id` 查找。其余 8 个 — `instagram_private`, `chat-widget`, `custom`, `email`, `line`, `imessage`, `linkedin` 和 `viber` — 没有可供查找的公共身份，因此在这些渠道上发送消息需要 `contact_id`；仅传递 `channel` 会返回 `400`，提示您需要 `contact_id`。

**cURL**（使用 `?apiKey=` 查询表单）

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/send?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "whatsapp",
    "phone_number": "+31612345678",
    "body": "Hi! Your appointment is confirmed."
  }'
```

**JavaScript**

```javascript
const res = await fetch("https://api.youraiconnector.com/v1/contacts/send", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    channel: "whatsapp",
    phone_number: "+31612345678",
    body: "Hi! Your appointment is confirmed.",
  }),
});
const data = await res.json();
console.log(data.message_id, data.channel);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/send",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "channel": "whatsapp",
        "phone_number": "+31612345678",
        "body": "Hi! Your appointment is confirmed.",
    },
)
data = res.json()
print(data["message_id"], data["channel"])
```

**响应** (`201 Created`)：

```json
{
  "success": true,
  "message_id": "aB3dE5fG7hI9jK1lM2nO",
  "contact_id": "contact123",
  "channel": "whatsapp"
}
```

> **消息可能被拒绝的原因：** 开启了“请勿打扰”或隐私模式的联系人无法接收外发消息 — 请求将失败并返回 `422`。如果没有联系人匹配您提供的 ID 或身份，您将收到 `404`。

---

## 列出联系人的消息

`GET /contacts/{contactId}/messages`

返回联系人的消息，按最新消息在前排序，并支持基于游标的分页。

| 查询参数 | 必填 | 说明 |
|---|---|---|
| `limit` | 否 | 页面大小。默认 `50`，最大 `100`。 |
| `cursor` | 否 | 上次响应中的 `next_cursor` 值。返回比该游标更早的消息。 |
| `filter` | 否 | 按内容类型过滤：`all`（默认）、`text`、`media` 或 `tool_use`。 |
| `direction` | 否 | 按方向过滤：`all`（默认）、`inbound`（从联系人接收）或 `outbound`（由您发送）。 |

> **关于过滤和分页的说明：** `filter` 和 `direction` 过滤器是在读取每一页后应用的，因此过滤后的页面包含的项目数可能少于 `limit`。但 `next_cursor` 仍会在整个对话中推进，因此请持续翻页直到 `next_cursor` 为 `null`。

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/contacts/contact123/messages?limit=50&direction=inbound" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({ limit: "50", direction: "inbound" });
const res = await fetch(
  `https://api.youraiconnector.com/v1/contacts/contact123/messages?${params}`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.messages, data.next_cursor);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"limit": 50, "direction": "inbound"},
)
data = res.json()
print(data["messages"], data["next_cursor"])
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "contact_id": "contact123",
  "messages": [
    {
      "id": "aB3dE5fG7hI9jK1lM2nO",
      "body": "Hi! Thanks for reaching out.",
      "direction": "inbound",
      "channel": "whatsapp",
      "status": "delivered",
      "type": null,
      "timestamp": "2026-06-01T10:00:00.000Z",
      "media_url": null,
      "media_content_type": null,
      "bot_reply": false
    }
  ],
  "next_cursor": "cD4eF6gH8iJ0kL2mN3oP"
}
```

### 消息字段

| 字段 | 描述 |
|---|---|
| `id` | 消息的唯一 ID。 |
| `body` | 消息的文本内容。 |
| `direction` | `inbound`（从联系人处接收）或 `outbound`（由您的账户发送）。 |
| `channel` | 发送或接收消息的渠道（例如 `whatsapp`、`sms`、`instagram`）。 |
| `status` | 当前投递状态，例如 `Created`、`sent`、`delivered`、`read`、`failed`。 |
| `type` | 消息类型。纯文本消息的类型为 `null`；自动化助手工具活动标记为 `tool_use`。 |
| `timestamp` | 消息创建的 ISO 8601 时间。 |
| `media_url` | 附件媒体文件的 URL（如有）。 |
| `media_content_type` | 附件媒体的 MIME 类型（如有）。 |
| `bot_reply` | 当消息由 AI 助手生成时为 `true`。 |
| `score` | 您对消息的评分：`1` 点赞，`-1` 点踩，`0` 表示尚未评分。请参阅 [评分或标星消息](#rate-or-star-a-message)。 |
| `is_important` | 当消息被标星时为 `true`。 |
| `is_deleted` | 当消息被删除时为 `true`。已删除的消息会保留在列表中，但其 `body` 和 `media_url` 为空。 |
| `reactions` | 双方对消息的表情符号反应。始终为一个数组——如果没有反应则为空。每个条目包含 `emoji`、`from_phone_number`、`from_me`（当反应是您自己时为 `true`）和 `reacted_at`。 |

---

## 列出聊天会话

聊天会话是与联系人的一个对话窗口：它在对方开始交谈时打开，在对话结束时关闭。会话是您将长历史记录分页为可读对话的方式，而不是将其显示为一个无尽的列表。

### 所有联系人的近期会话

`GET /chat-sessions/recent`

返回账户中所有联系人在过去 X 小时内开始的会话，按最新时间排序。

| 查询参数 | 必需 | 描述 |
|---|---|---|
| `hours` | 是 | 向前回溯的小时数。必须是正整数。 |
| `status` | 否 | 仅返回具有此状态的会话：`ChatSessionOpened` 或 `ChatSessionClosed`。 |
| `limit` | 否 | 返回会话的最大数量。默认 `100`，最大 `100`。 |
| `includeMessages` | 否 | `true` 会为每个会话添加一个 `messages` 数组。默认关闭，因为这会使响应变得非常大。 |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/chat-sessions/recent?hours=24&status=ChatSessionClosed" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const params = new URLSearchParams({ hours: "24", status: "ChatSessionClosed" });
const res = await fetch(`https://api.youraiconnector.com/v1/chat-sessions/recent?${params}`, {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.data.total_sessions, data.data.sessions);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/chat-sessions/recent",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"hours": 24, "status": "ChatSessionClosed"},
)
data = res.json()["data"]
print(data["total_sessions"], data["sessions"])
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "data": {
    "hours_ago": 24,
    "total_sessions": 2,
    "sessions": [
      {
        "session_id": "session456",
        "contact_id": "contact123",
        "contact_name": "Jane Doe",
        "contact_phone": "+31612345678",
        "contact_email": "jane@example.com",
        "start_date_time": "2026-06-01T09:55:00.000Z",
        "end_date_time": "2026-06-01T10:20:00.000Z",
        "status": "ChatSessionClosed",
        "tag": "Booking enquiry"
      }
    ]
  }
}
```

### 单个联系人的所有会话

`GET /chat-sessions/{contactId}`

返回单个联系人的所有聊天会话。与上述相同的 `status`、`limit` 和 `includeMessages` 参数——`hours` 在此处不适用。

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/chat-sessions/contact123?limit=20" \
  -H "X-API-Key: YOUR_API_KEY"
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "data": {
    "contact_id": "contact123",
    "contact_name": "Jane Doe",
    "total_sessions": 2,
    "sessions": [
      {
        "id": "session456",
        "start_date_time": "2026-06-01T09:55:00.000Z",
        "end_date_time": "2026-06-01T10:20:00.000Z",
        "status": "ChatSessionClosed",
        "tag": "Booking enquiry"
      }
    ]
  }
}
```

> **两个端点之间的会话 ID 字段名称不同。** 近期会话列表将其称为 `session_id`（它还携带联系人的详细信息，因为会话来自许多联系人）；按联系人列表将其称为 `id`。在获取下方完整线程时，无论哪个值，您都可以将其作为 `{sessionId}` 传入。

当 `includeMessages=true` 时，每个会话都会获得一个 `messages` 数组，其条目包含 `id`、`body`、`direction`、`timestamp`、`type`、`channel` 和 `status`。

---

## 获取聊天会话线程

`GET /contacts/{contactId}/chat-sessions/{sessionId}/messages`

聊天会话将联系人的消息归入一个对话窗口。此端点返回单个会话的完整线索（**按时间从旧到新排序**），以及会话的元数据。您可以通过聊天会话端点找到联系人的会话 ID。

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/contacts/contact123/chat-sessions/session456/messages" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/chat-sessions/session456/messages",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.session, data.messages);
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/contacts/contact123/chat-sessions/session456/messages",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
print(data["session"], data["messages"])
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "contact_id": "contact123",
  "session": {
    "id": "session456",
    "status": "ChatSessionClosed",
    "start_date_time": "2026-06-01T09:55:00.000Z",
    "end_date_time": "2026-06-01T10:20:00.000Z",
    "tag": "Booking enquiry"
  },
  "messages": [
    {
      "id": "aB3dE5fG7hI9jK1lM2nO",
      "body": "Hi! Thanks for reaching out.",
      "direction": "inbound",
      "channel": "whatsapp",
      "status": "delivered",
      "type": null,
      "timestamp": "2026-06-01T09:55:00.000Z",
      "media_url": null,
      "media_content_type": null,
      "bot_reply": false
    }
  ]
}
```

`session` 对象报告了 `status`（激活时为 `ChatSessionOpened`，结束后为 `ChatSessionClosed`）、`start_date_time`、`end_date_time` 以及人类可读的 `tag`。`messages` 数组使用与列表端点相同的 [消息字段](#message-fields)。

---

## 编辑、删除和对消息做出反应

这些端点会在消息发送后对其进行更改。其中两个端点不仅会触达您自己的副本，还会触达联系人的渠道，因此在连接它们之前请阅读章节介绍——具体可行性完全取决于对话所在的渠道。

**每个渠道允许的操作**

| 操作 | 可更改联系人副本的渠道 | 时间限制 |
|---|---|---|
| 编辑已发送消息 | 聊天小部件、WhatsApp Web、Telegram、LinkedIn | 聊天小部件无限制，WhatsApp Web 为 15 分钟，Telegram 为 48 小时，LinkedIn 为 60 分钟 |
| 为所有人删除 | 聊天小部件、WhatsApp Web、Telegram、LinkedIn | LinkedIn 为 60 分钟；其他渠道无公开限制 |
| 使用表情符号反应 | WhatsApp Web、Telegram | 无 |

在所有其他渠道（WhatsApp Business API、SMS、Instagram、Messenger、电子邮件、LINE、自定义渠道）上，删除操作仍会从您的收件箱中移除消息，但联系人会保留其副本，且完全无法进行编辑或反应。

### 编辑消息

`POST /contacts/{contactId}/messages/{messageId}/edit`

重写您已发送的消息，同时在联系人的设备和您的副本中进行更新。

| 字段 | 必填 | 说明 |
|---|---|---|
| `body` | 是 | 新的消息文本。不能为空，且最多可包含 4096 个字符。 |

与删除不同，当渠道拒绝时，此操作会**明确报错**：您将收到一个 `409`，且您的副本将保持与联系人端完全一致，因为显示对方从未收到的编辑内容会导致双方不同步。`edit_reason` 字段会告知您原因——渠道的编辑窗口已关闭、渠道已断开连接或发生了其他错误。

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/edit" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "body": "Sorry - I meant Thursday at 3pm." }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/edit",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ body: "Sorry - I meant Thursday at 3pm." }),
  }
);
const data = await res.json();
console.log(data.edited, data.edit_reason);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/edit",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"body": "Sorry - I meant Thursday at 3pm."},
)
data = res.json()
print(data.get("edited"), data.get("edit_reason"))
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "contact_id": "contact123",
  "message_id": "msg_1",
  "edited": true,
  "edit_reason": "edit_dispatched"
}
```

如果渠道不接受该编辑，您将收到一个 `409`，且没有任何内容被更改：

```json
{
  "success": false,
  "error": "The message could not be edited",
  "edit_reason": "channel_disconnected"
}
```

已删除的消息、完全不支持编辑的渠道以及对于其渠道而言过旧的消息都会返回 `400` —— 请求从未到达该渠道。

### 删除单条消息

`DELETE /contacts/{contactId}/messages/{messageId}`

从您的对话中移除消息，并在渠道允许的情况下，同时撤回联系人的副本。无需请求正文。

当消息存在时，此操作总是返回 `200`，即使无法撤回联系人的副本——因为您的副本**确实**已经删除，所以报错会产生误导。请阅读响应中的三个字段，以告知用户实际发生了什么：

| 字段 | 说明 |
|---|---|
| `revoke_supported` | 此渠道是否支持撤回消息。 |
| `revoked` | 联系人设备上的副本是否已被移除。 |
| `revoke_reason` | 当 `revoked` 为 `false` 时，未被移除的原因——例如 `revoke_window_closed` 或 `already_deleted`。 |

**cURL**

```bash
curl -X DELETE "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1",
  { method: "DELETE", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.revoked, data.revoke_reason);
```

**Python**

```python
import requests

res = requests.delete(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
data = res.json()
print(data["revoked"], data["revoke_reason"])
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "contact_id": "contact123",
  "message_id": "msg_1",
  "revoke_supported": true,
  "revoked": true,
  "revoke_reason": "revoke_dispatched"
}
```

> 已删除的消息不会从对话历史记录中移除。它们会保留在 `GET /contacts/{contactId}/messages` 中，并带有 `is_deleted: true` 以及空的 `body` 和 `media_url`。

### 一次性删除多条消息

`POST /contacts/{contactId}/messages/bulk-delete`

仅从您的一侧清除一批消息。消息正文和附件会被清空，但**联系人设备上的内容不会被撤回**——若要同时撤回消息，请使用上述单条消息端点逐一删除。

| 字段 | 必填 | 说明 |
|---|---|---|
| `message_ids` | 是 | 一个非空的消息 ID 数组，每次请求最多 500 个。`messageIds` 可作为别名使用。 |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/messages/bulk-delete" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message_ids": ["msg_1", "msg_2"] }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/messages/bulk-delete",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ message_ids: ["msg_1", "msg_2"] }),
  }
);
console.log((await res.json()).deleted);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/bulk-delete",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"message_ids": ["msg_1", "msg_2"]},
)
print(res.json()["deleted"])
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "contact_id": "contact123",
  "deleted": 2
}
```

### 对消息做出反应

`POST /contacts/{contactId}/messages/{messageId}/react`

在消息上添加您自己的表情符号反应，或通过发送空字符串来撤回反应。联系人自己的反应永远不会受到影响。

| 字段 | 必填 | 说明 |
|---|---|---|
| `emoji` | 是 | 要添加的表情符号，或使用 `""` 来移除您的反应。必须是单个字符串，不含空格，最多 16 个字符。 |

与编辑操作一样，如果联系人从未收到该消息，此操作会失败而不是显示反应，并且失败信息会告知您是否值得重试：

- `422` — 此对话永远无法送达：频道不支持反应、消息没有频道端 ID，或者表情符号超出了该频道允许的范围。
- `409` — 频道暂时无法访问。重试可能会成功。

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/react" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "emoji": "👍" }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/react",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ emoji: "👍" }),
  }
);
const data = await res.json();
console.log(data.reactions);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1/react",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"emoji": "👍"},
)
print(res.json()["reactions"])
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "contact_id": "contact123",
  "message_id": "msg_1",
  "reaction_supported": true,
  "reaction_reason": "reaction_dispatched",
  "reactions": [
    {
      "emoji": "👍",
      "from_phone_number": "+31612345678",
      "from_me": true,
      "reacted_at": "2026-06-01T10:05:00.000Z"
    }
  ]
}
```

`reactions` 数组是当前消息上所有反应的完整集合，包括您和联系人的反应。在 `409` 或 `422` 情况下，它会原样返回，因此直接根据该数组进行渲染的客户端永远不会显示未送达的反应。

### 评价或标记消息

`PATCH /contacts/{contactId}/messages/{messageId}`

对消息进行点赞或点踩评价，和/或将其标记为重要。这仅是您一侧的记录工作——不会向联系人发送任何内容。

| 字段 | 必填 | 说明 |
|---|---|---|
| `score` | 否 | `1` 点赞，`-1` 点踩，`0` 清除评分。 |
| `is_important` | 否 | `true` 收藏消息，`false` 取消收藏。必须是真实的布尔值，而不是字符串 `"true"`。 |

至少发送其中一个，否则会收到 `400`。只会写入你发送的内容，因此收藏消息永远不会清除其评分，反之亦然——响应只会回显你发送的字段。

**cURL**

```bash
curl -X PATCH "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "score": 1, "is_important": true }'
```

**JavaScript**

```javascript
await fetch("https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1", {
  method: "PATCH",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ score: 1, is_important: true }),
});
```

**Python**

```python
import requests

requests.patch(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/msg_1",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"score": 1, "is_important": True},
)
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "contact_id": "contact123",
  "message_id": "msg_1",
  "score": 1,
  "is_important": true
}
```

---

## 将消息标记为已读

您可以清除特定消息或整个对话的未读状态。

### 将特定消息标记为已读

`POST /contacts/{contactId}/messages/mark-read`

传入要标记为已读的消息 ID。

| 字段 | 必需 | 说明 |
|---|---|---|
| `message_ids` | 是 | 非空的消息 ID 数组（每次请求最多 500 个）。 |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/messages/mark-read" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "message_ids": ["aB3dE5fG7hI9jK1lM2nO", "cD4eF6gH8iJ0kL2mN3oP"]
  }'
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/messages/mark-read",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      message_ids: ["aB3dE5fG7hI9jK1lM2nO", "cD4eF6gH8iJ0kL2mN3oP"],
    }),
  }
);
const data = await res.json();
console.log(data.marked_read);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/messages/mark-read",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"message_ids": ["aB3dE5fG7hI9jK1lM2nO", "cD4eF6gH8iJ0kL2mN3oP"]},
)
print(res.json()["marked_read"])
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "contact_id": "contact123",
  "marked_read": 2
}
```

### 将整个聊天标记为已读

`POST /contacts/{contactId}/mark-read`

清除收件箱中联系人整个对话的未读标记。无需请求正文。

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/mark-read" \
  -H "X-API-Key: YOUR_API_KEY"
```

**JavaScript**

```javascript
const res = await fetch(
  "https://api.youraiconnector.com/v1/contacts/contact123/mark-read",
  { method: "POST", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.success);
```

**Python**

```python
import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/contacts/contact123/mark-read",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["success"])
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "contact_id": "contact123"
}
```

### 将整个聊天标记为未读

`POST /contacts/{contactId}/mark-unread`

将未读标记重新放回对话上——当团队成员打开了聊天但又将其交回时非常有用。无需请求正文。

这是一个仅限收件箱的标记：它**不会**更改对话的最后阅读时间，因此在支持阅读回执的渠道上，不会向联系人发送阅读回执。

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/mark-unread" \
  -H "X-API-Key: YOUR_API_KEY"
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "contact_id": "contact123"
}
```

---

## 导出对话

导出功能为你提供整个对话的可读副本，而无需逐页翻阅消息。每个导出端点都接受一个 `filter`，可选值为 `all`（默认）、`text`、`media` 或 `tool_use`，与消息列表上的过滤器相匹配。

### 导出单个联系人的聊天记录

`GET /chat-exports/{contactId}`

| 查询参数 | 必填 | 说明 |
|---|---|---|
| `format` | 否 | `txt`（默认）返回纯文本副本的下载链接。`json` 在响应中以结构化数据形式返回消息。 |
| `filter` | 否 | `all`（默认）、`text`、`media` 或 `tool_use`。 |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/chat-exports/contact123?format=json" \
  -H "X-API-Key: YOUR_API_KEY"
```

**Python**

```python
import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/chat-exports/contact123",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"format": "json"},
)
print(res.json()["data"]["messages"])
```

**响应包含 `format=json`** (`200 OK`)：

```json
{
  "success": true,
  "data": {
    "contact": {
      "id": "contact123",
      "name": "Jane Doe",
      "phone": "+31612345678",
      "email": "jane@example.com"
    },
    "messages": [
      {
        "body": "Hi! I have a question about my order.",
        "direction": "inbound",
        "timestamp": "2026-06-01T09:55:00.000Z",
        "type": "text",
        "media_url": null,
        "media_content_type": null,
        "name": null,
        "args": null
      }
    ]
  }
}
```

使用 `format=txt`（默认值）时，`data` 则是生成的副本文件的下载链接：

```json
{
  "success": true,
  "data": "https://storage.googleapis.com/.../chat-export-contact123-....txt"
}
```

> **下载链接有效期较短。** 获取链接后请立即下载文件，不要存储链接——当再次需要副本时，请重新请求导出。

### 导出所有近期对话

`GET /chat-exports/recent`

通过单次调用，导出过去 X 小时内所有活跃联系人的对话。

| 查询参数 | 必填 | 描述 |
|---|---|---|
| `hours` | 是 | 回溯查看过去多少小时内的活动。必须为正整数。 |
| `format` | 否 | `json`（默认）为每个联系人返回一个条目。`txt` 返回一个包含所有对话的单一可下载文本文件。 |
| `limit` | 否 | 要导出的最大联系人数量。默认 `50`，最大 `100`。 |
| `filter` | 否 | `all`（默认）、`text`、`media` 或 `tool_use`。 |

**cURL**

```bash
curl "https://api.youraiconnector.com/v1/chat-exports/recent?hours=24&limit=25" \
  -H "X-API-Key: YOUR_API_KEY"
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "data": {
    "hours_ago": 24,
    "total_contacts": 2,
    "exports": [
      {
        "contactId": "contact123",
        "contactName": "Jane Doe",
        "phoneNumber": "+31612345678",
        "email": "jane@example.com",
        "messageCount": 12,
        "chatExport": "Acme Export - Jane Doe\nPhone: +31612345678\n..."
      }
    ]
  }
}
```

使用 `format=txt` 时，响应内容即为文本文件本身，以文件下载形式发送，而非 JSON 格式。

> 此单次调用会拉取每个匹配联系人的完整历史记录，因此在繁忙账户上请保持 `hours` 和 `limit` 的数值适中。

### 通过电子邮件向联系人发送对话记录

`POST /chat-exports/{contactId}/email`

通过电子邮件向联系人发送其对话记录——即由您自己的系统驱动的“将此聊天记录发送给我”流程。

| 字段 | 必填 | 描述 |
|---|---|---|
| `recipient_email` | 否 | 发送地址。默认为联系人存储的电子邮件地址。 |
| `via` | 否 | `auto`（默认）选择最佳路由，`transactional` 以系统邮件形式发送，`email_channel` 通过您已连接的电子邮件渠道发送。 |
| `note` | 否 | 您在对话记录上方显示的一行简短文字。最多 1000 个字符。 |

**cURL**

```bash
curl -X POST "https://api.youraiconnector.com/v1/chat-exports/contact123/email" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "note": "Here is a copy of our chat, as promised." }'
```

**响应** (`200 OK`)：

```json
{
  "success": true,
  "data": {
    "via": "transactional",
    "recipientEmail": "jane@example.com",
    "messageCount": 42,
    "omittedCount": 0
  }
}
```

`omittedCount` 会告知您为了保持邮件长度合理而省略了多少条最早的消息。`200` 表示对话记录已生成并排队等待发送，并不代表邮件已送达收件箱。

---

## 为单个联系人暂停或恢复 AI

`PUT /contacts/{contactId}`

将 `is_bot_active` 设置为 `false` 可停止 AI 回复某个联系人，将其改回 `true` 即可交还对话控制权。当人工介入对话时，这是您需要的接管开关：即使机器人处于暂停状态，您通过 API 发送的消息仍会正常送达。

**cURL**

```bash
curl -X PUT "https://api.youraiconnector.com/v1/contacts/contact123" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "is_bot_active": false }'
```

**JavaScript**

```javascript
await fetch("https://api.youraiconnector.com/v1/contacts/contact123", {
  method: "PUT",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ is_bot_active: false }),
});
```

**Python**

```python
import requests

requests.put(
    "https://api.youraiconnector.com/v1/contacts/contact123",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={"is_bot_active": False},
)
```

**响应**

```json
{
  "success": true,
  "contact_id": "contact123"
}
```

**在回复时暂停**

如果人工通过发送回复来接管对话，您可以在同一请求中暂停机器人，而无需进行第二次调用。`POST /contacts/{contactId}/send-message` 接受两个可选标志：

| 字段 | 描述 |
|---|---|
| `pauseBot` | `true` 在发送消息的同时为该联系人暂停 AI。 |
| `clearIncompleteReply` | `true` 丢弃未完成的机器人回复，以便之后不会恢复发送。 |

```bash
curl -X POST "https://api.youraiconnector.com/v1/contacts/contact123/send-message" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "body": "Hi, Sarah here - taking over from the assistant.",
    "pauseBot": true,
    "clearIncompleteReply": true
  }'
```

当应用暂停时，响应中会包含 `"botPaused": true`。

> 使用 [`POST /contacts/bulk-flag`](contacts.md) 将联系人标记为私密也会暂停该联系人的机器人服务。有关完整字段列表，请参阅 [联系人](contacts.md)。

---

## 构建您自己的收件箱

收件箱所需的一切功能都在本页面及 [联系人](contacts.md) 中：

| 所需操作 | 端点 |
|---|---|
| 列出对话 | `GET /contacts` |
| 读取对话 | `GET /contacts/{contactId}/messages` |
| 列出联系人的聊天会话 | `GET /chat-sessions/{contactId}` |
| 查看最近收到的内容 | `GET /chat-sessions/recent` |
| 读取单个聊天会话 | `GET /contacts/{contactId}/chat-sessions/{sessionId}/messages` |
| 发送手动回复 | `POST /contacts/{contactId}/send-message` |
| 更正刚发送的回复 | `POST /contacts/{contactId}/messages/{messageId}/edit` |
| 删除消息 | `DELETE /contacts/{contactId}/messages/{messageId}` |
| 清除多条消息 | `POST /contacts/{contactId}/messages/bulk-delete` |
| 使用表情符号做出反应 | `POST /contacts/{contactId}/messages/{messageId}/react` |
| 评价或标记消息 | `PATCH /contacts/{contactId}/messages/{messageId}` |
| 标记为已读 | `POST /contacts/{contactId}/mark-read` |
| 将聊天交回给团队 | `POST /contacts/{contactId}/mark-unread` |
| 导出对话记录 | `GET /chat-exports/{contactId}` |
| 暂停或恢复 AI | `PUT /contacts/{contactId}` 使用 `is_bot_active` |

如需实时更新，请使用 [Webhooks](webhooks.md) 订阅 `New Message`、`Replies`、`Human Alerted` 和 `Chat Concluded` 事件，而不是通过定时轮询此 API。

---

## 消息 API 错误

消息端点返回标准的错误信封：

```json
{
  "success": false,
  "error": "Contact not found"
}
```

| 状态 | 在消息端点上发生的情况 |
|---|---|
| `400` | 缺少必填字段或参数无效（错误的 `limit`、`hours`、`filter`、`direction`、`status`，空的或超过 500 个的 `message_ids` 数组，无效的 `cursor`，空的或过长的编辑 `body`，超出 `-1`/`0`/`1` 范围的 `score`，或带有空格或超过 16 个字符的表情符号）。当消息完全无法编辑时也会返回此状态——消息已被删除、其渠道不支持编辑，或已超出该渠道的编辑时限。 |
| `404` | 未找到联系人、聊天会话或提供的其中一个消息 ID。 |
| `409` | 渠道目前无法接受此更改。未写入任何内容：若是编辑操作，`edit_reason` 会说明原因；若是反应操作，则表示渠道暂时无法连接，重试可能会成功。 |
| `422` | 联系人无法接收出站消息（请勿打扰、私密或不支持的渠道），或者此对话中无法传递反应（`reaction_reason` 会说明原因）。 |

每个端点都可能返回的共享代码 — `401`, `403`（您的套餐不包含 API 访问权限）, `429`（速率限制）和 `500` — 及其重试指南列在 [错误与分页](errors-and-pagination.md) 中。

---

## 后续步骤

- [Webhooks](webhooks.md) — 获取推送的发送状态更新，无需轮询。
- [Contacts](contacts.md) — 创建并查找您联系的对象。
- [Appointments](appointments.md) — 为您的联系人预约并管理日程。
