xiyebot API 文档
API_DOC_VERSION 2026.09.08.1 源文件:docs/CUSTOMER_CC_API_GUIDE.md

xiyebot 微信个人号 API 对接指南(客户实例 Claude Code 版)

文档版本:2026.09.09.1

在线镜像:https://flowbot.efguc.com/api-docs/customer

源文件真相:xiyebot 仓库 docs/CUSTOMER_CC_API_GUIDE.md(希也家 SaaS 交付包里的 /opt/agentos/docs/xiyebot_API对接指南_客户ClaudeCode版.md 是它的同步副本)

> 这份文档写给客户实例服务器上的 Claude Code(下称"你")看。

> 你所在的系统是私域 AgentOS 定制版(/opt/agentos/,先读同目录 CLAUDE.md)。

> 它的"个微通道"——AI 员工用真实微信个人号收发消息、发朋友圈、发视频号——由一套独立的执行系统 xiyebot 提供:

> 一部装了微信的安卓手机 + 手机上的 Xiyebot Agent App + 香港的 xiyebot 服务端。

> 本文是你与 xiyebot 之间的完整契约:哪些接口能调、怎么调、怎么拿结果、哪些事绝对不能做。全文照做,不要猜。

---

0. 心智模型(先读懂再动手)

┌─ 客户实例 /opt/agentos(平台 dist 闭源)────────────────────────────┐
│  · 后台「机器人管理」登记 robotId(授权码)                          │
│  · 引擎已把每个 robotId 的回调地址指到本实例 /api/flowbot/callback  │
│  · 收到入站消息 → 销冠/售后 AI 自动接待(你不用写)                   │
└─────────────▲──────────────────────────────┬──────────────────────┘
              │ 回调(receipt/callBack/logs)   │ HTTPS  POST /wx/api/sendTask?robotId=…
              │                              ▼
┌─ xiyebot 服务端 https://flowbot.efguc.com ─────────────────────────┐
│  任务入库 → 钉到 robotId 绑定的手机 → 手机轮询领任务 → 无障碍操作微信 │
└─────────────────────────────▲───────────────────────────────────────┘
                              │ 心跳 / 领任务 / 回传结果
                    ┌─────────┴─────────┐
                    │ 安卓手机 + 微信    │  ← 一个 robotId(授权码)= 一台手机 = 一个微信号
                    │ Xiyebot Agent App │
                    └───────────────────┘

五个事实:

1. robotId 就是授权码。格式 xyb + 32 位十六进制。它同时是手机 App 里激活设备用的码、xiyebot 侧的设备身份、本实例后台登记的机器人 ID。一码一机:一个授权码永远只对应一台手机。

2. 调用只需要 robotId,没有别的密钥。/wx/api/* 这组接口用 URL 上的 robotId 鉴权,授权码被平台禁用后立即全部 401。

3. xiyebot 是执行器,不是对话系统。它只做"把任务下发到手机、在微信里点出来、把结果回传"。对话逻辑(收到消息怎么回)全在本实例引擎的 AI 员工里,通过后台「AI 角色配置」或 custom/ 调整,不要拿 xiyebot 自己再搭一套自动回复。

4. 回调归平台引擎,你收不到。每个 robotId 的回调地址已由引擎登记为本实例,入站消息、任务终态都投到引擎。你的代码要知道任务结果,用本文 §3.5 的 getTaskStatus 轮询。

5. 这是一台真实手机上的真实微信。每个动作都是无障碍点击,慢(一条文字 5~15 秒)、串行、受微信风控约束。按"人在操作手机"的节奏设计你的调用,不要按 HTTP API 的节奏。

你能用 xiyebot 做什么(适合二开的场景)

| 场景 | 用哪个接口 | 说明 |

|---|---|---|

| 主动推送:订单通知、物流提醒、活动提醒、报表推送到某个好友/群 | sendTask type 10001/10002/10006 | 最常见。客户自建业务板块产生的事件 → 发微信 |

| 内容分发:定时发朋友圈、发视频号 | sendTask type 10020/10021 | 配合平台的成片/海报工厂产出的 OSS 地址 |

| 群运营:群里 @ 某人 | sendTask type 50009 | |

| 拉新/维护:加好友、改备注 | sendTask type 20005/20004 | 加好友需对方通过,结果在 getTaskStatus |

| 看这个微信号最近聊了什么 | getChatHisList | xiyebot 侧留存的可见消息,只读 |

| 判断微信号是否在线 | getRobotInfo | 做健康面板、发前预检 |

| 查通讯录好友/群 | sendTask type 20007 + getTaskStatus | 结果在 result.contacts[] |

不该用 xiyebot 做什么

---

1. 接入信息

1.1 地址与鉴权

基址:见 /opt/agentos/.env 的 XIYEBOT_GEWEI_BASE(装机默认 https://flowbot.efguc.com/wx)
路径:基址 + /api/<接口名>
鉴权:query 参数 robotId=<授权码>;POST 时也可放在 JSON body 的 robotId 字段
Content-Type: application/json

1.2 robotId 在哪里

本实例后台「机器人管理」(或「租户中心 → 绑定机器人」)登记的每台机器人都在本库 flowbot_robots 表:

-- 用只读账号(CLAUDE.md §1.5 的 myt_ro)查:
SELECT robotId, nickname, wechatId, channel, paused, agentType
FROM flowbot_robots
WHERE unboundAt IS NULL AND paused = 0;

1.3 响应信封(FlowBot 同形)

所有 /wx/api/* 接口返回:

{ "code": 200, "message": "指令发送成功", "data": { ... } }

| HTTP / code | 含义 | 你该怎么办 |

|---|---|---|

| 200 | 成功 | |

| 400 | 参数错(message 里有具体字段,如 taskList[0].message不能为空) | 修参数,不要重试 |

| 401 robotId无效 | 授权码不存在、被禁用、格式错 | 核对 flowbot_robots;仍不对找平台 |

| 404 任务不存在 | getTaskStatus 查不到,或任务不属于这个 robotId | 核对 taskId 与 robotId 配对 |

| 5xx / 超时 | xiyebot 服务端异常 | 指数退避重试(带同一个 idempotencyKey) |

---

2. 先做健康检查:getRobotInfo

GET  {BASE}/api/getRobotInfo?robotId=<授权码>
POST {BASE}/api/getRobotInfo?robotId=<授权码>
{
  "code": 200,
  "message": "查询成功",
  "data": {
    "robotId": "xyb1f2a…",
    "robotName": "莉莉安",
    "deviceId": "dev_xxx",
    "deviceName": "莉莉安",
    "status": "online",
    "online": true,
    "isOnline": true,
    "agentVersion": "0.6.3",
    "wechatVersion": "unknown",
    "onlineTtl": 29
  }
}

---

3. 发任务:sendTask

POST {BASE}/api/sendTask?robotId=<授权码>
Content-Type: application/json
{
  "taskList": [
    { "type": 10001, "searchText": "超逸", "message": "您好,您的订单已发货,单号 SF123456。" }
  ]
}

响应(每个 task 一个 taskId,拿到 200 只表示入库排队,不表示已发出):

{
  "code": 200,
  "message": "指令发送成功",
  "data": {
    "taskList": [
      { "taskId": "task_xxx", "status": "pending", "searchText": "超逸", "type": 10001 }
    ]
  }
}

3.1 每个 task 的通用字段

| 字段 | 必填 | 说明 |

|---|---|---|

| type | 是 | 任务类型,见 §3.2 |

| searchText | 视类型 | 微信里显示的联系人/群名,必须一字不差(备注名优先于昵称,微信搜索框能搜到什么就填什么)。朋友圈/视频号/通讯录类型可不填 |

| targetType | 否 | friend(默认)或 group。发群消息、群 @ 必须填 group |

| message | 视类型 | 正文、图片 URL、文件 URL……随类型变化 |

| idempotencyKey | 强烈建议 | 你这条业务消息的稳定唯一 ID(如 myt-order-123-shipped)。重试带同一个值,xiyebot 不会重复发 |

| deviceId | 否 | 不填。xiyebot 按 robotId 自动钉到绑定的手机 |

| timeoutSeconds | 否 | 默认 90。手机离线时任务最多等这么久,之后 timeout |

| priority | 否 | 默认 5,数字越小越先执行 |

顶层还可带 timeoutSeconds / priority / contactTypes,对整个 taskList 生效。

3.2 任务类型总表

| type | 内部类型 | 作用 | 关键字段 | 手机上发生什么 |

|---|---|---|---|---|

| 10001 | send_text | 发文字 | searchText, message | 搜索联系人 → 进会话 → 输入 → 发送 |

| 10002 | send_image | 发图片 | searchText, message=图片 URL, fileName 可选 | 下载图片入相册 → 会话里从相册发送 |

| 10006 | send_file | 发文件 | searchText, fileUrl/url/message=文件 URL, fileName, mimeType | 下载后以文件形式分享到会话 |

| 10010 | send_miniprogram | 发小程序链接文本 | searchText, message=#小程序://…, sendMode=mini_program_link, title, description | 发的是可点击的链接文字,不是卡片 |

| 10020 | publish_moments | 发朋友圈(图文) | 多图必须用 imageUrls[](≤9);imageUrl/message=单张图 URL(只发 1 张), caption 文案, dryRun | 图片入相册 → 朋友圈 → 选图 → 填文案 → 发表 |

| 10021 | publish_channel_video | 发视频号 | videoUrl(http/https), caption/description/message 文案, fileName, videoIndex | 视频下载入相册 → 视频号发布页 → 选最新一格 → 发布 |

| 10022 | publish_douyin_video | 抖音发布视频(仅发布) | videoUrl, caption/description/message, fileName, dryRun | 视频入相册 → 拉起抖音发布 → 填文案 → 发布;searchText 可不填 |

| 10023 | publish_xhs_video | 小红书发布视频(仅发布) | videoUrl, title, caption/description/message, fileName, dryRun | 视频入相册 → 拉起小红书发布 → 标题+正文 → 发布;searchText 可不填 |

| 20004 | update_remark | 改好友备注 | searchText=当前显示名, remark/remarkName/message=新备注 | |

| 20005 | add_friend | 加好友 | searchText(或 phone/wxid/account)=手机号/微信号/昵称, message/verifyMessage=验证语 | 发出申请;对方通过与否 xiyebot 不知道 |

| 20006 | friend_detail | 读好友资料 | searchText | 结果在 getTaskStatus 的 result.profile |

| 20007 | read_contacts | 同步通讯录 | contactTypes=["friend","group"], maxItems(默认 500,≤5000), maxScrolls(默认 30,≤200) | 翻通讯录页,结果在 result.contacts[]。很慢(几分钟),一天最多跑一两次 |

| 50009 | group_at | 群里 @ 某人 | searchText=群名, targetType=group, atName/at/memberName=被 @ 的人, message=正文 | |

不存在的 type 会 400。FORCE_OFFLINE、getChatHisList 之外的 FlowBot 原厂接口在 xiyebot 上不存在或被拒绝。

3.3 各类型请求样例

发文字给好友

{ "taskList": [ { "type": 10001, "searchText": "超逸", "message": "您好,您的订单已发货。", "idempotencyKey": "myt-order-1001-shipped" } ] }

发文字到群

{ "taskList": [ { "type": 10001, "targetType": "group", "searchText": "明杨VIP客户群", "message": "今晚 8 点直播,记得来。" } ] }

发图片(URL 必须公网可访问,http/https;客户实例的 OSS_CDN_URL 地址即可)

{ "taskList": [ { "type": 10002, "searchText": "超逸", "message": "https://cdn.example.com/poster/1001.jpg", "fileName": "poster-1001.jpg" } ] }

发文件

{ "taskList": [ { "type": 10006, "searchText": "超逸", "fileUrl": "https://cdn.example.com/report/2026-09.pdf", "fileName": "9月对账单.pdf", "mimeType": "application/pdf" } ] }

群 @ 某人

{ "taskList": [ { "type": 50009, "targetType": "group", "searchText": "明杨VIP客户群", "atName": "超逸", "message": "您的订单有更新,请查收。" } ] }

发朋友圈(多图)

> ⚠️ 发多图必须用 imageUrls 数组(最多 9 张)。 用 message 或 imageUrl(单个字符串)

> 只会发第 1 张——这是「N 张只发 1 张」最常见的原因。把每张图 URL 都放进 imageUrls、

> 文案放 caption 即可;服务端会全部下相册、多选发九宫格。

{
  "taskList": [ {
    "type": 10020,
    "imageUrls": [
      "https://cdn.example.com/1.jpg",
      "https://cdn.example.com/2.jpg",
      "https://cdn.example.com/3.jpg"
    ],
    "caption": "新品到货,今日下单赠小样。",
    "dryRun": false
  } ]
}

dryRun=true:手机会选图、填好文案、停在发表前不点发表。上线一个新的自动发圈流程前,先 dryRun 一轮,让客户在手机上看一眼版式。

发视频号

{
  "taskList": [ {
    "type": 10021,
    "videoUrl": "https://cdn.example.com/video/reel-2026-09-08.mp4",
    "caption": "秋季新品开箱 #明杨天下",
    "fileName": "reel-2026-09-08.mp4"
  } ]
}

videoUrl 不传时,手机会发相册里第 videoIndex(默认 0 = 最新)个视频——只在客户手动往手机相册放了视频时才这么用。

加好友

{ "taskList": [ { "type": 20005, "phone": "13800000000", "message": "您好,我是明杨天下客服小杨,加个微信方便给您发物流信息。" } ] }

同步通讯录

{ "taskList": [ { "type": 20007, "contactTypes": ["friend", "group"], "maxItems": 1000 } ] }

3.4 一次请求放几个 task

3.5 拿结果:getTaskStatus

GET  {BASE}/api/getTaskStatus?robotId=<授权码>&taskId=task_xxx
POST {BASE}/api/getTaskStatus?robotId=<授权码>   body: { "taskId": "task_xxx" }
{
  "code": 200,
  "message": "查询成功",
  "data": {
    "taskId": "task_xxx",
    "type": 10001,
    "taskType": "send_text",
    "status": "succeeded",
    "finished": true,
    "success": true,
    "searchText": "超逸",
    "targetType": "friend",
    "result": { "sent": true },
    "errorCode": null,
    "errorMessage": null,
    "createdAt": "2026-09-08T10:00:00+00:00",
    "updatedAt": "2026-09-08T10:00:12+00:00",
    "finishedAt": "2026-09-08T10:00:12+00:00"
  }
}

---

4. 读消息:getChatHisList(只读、排障用)

GET  {BASE}/api/getChatHisList?robotId=<授权码>&searchText=超逸&limit=20
POST {BASE}/api/getChatHisList?robotId=<授权码>   body: { "searchText": "超逸", "limit": 20 }
{
  "code": 200,
  "message": "查询成功",
  "data": {
    "robotId": "xyb…",
    "list": [
      { "searchText": "超逸", "senderName": null, "role": "user", "type": "text", "message": "在吗", "visibleTime": null, "clientMessageKey": "…", "createdAt": "2026-09-08T09:58:00+00:00" },
      { "searchText": "超逸", "senderName": null, "role": "assistant", "type": "text", "message": "在的,您说。", "visibleTime": null, "clientMessageKey": "…", "createdAt": "2026-09-08T09:58:20+00:00" }
    ]
  }
}

---

5. 回调:平台引擎已经接管,你不要动

xiyebot 对每个 robotId 保存一个回调地址(updateCallBackUrl),装机/绑定机器人时引擎已把它设为本实例的 {APP_BASE_URL}/api/flowbot/callback。之后 xiyebot 往这个地址推:

| mode | 时机 | 谁消费 |

|---|---|---|

| init / online | 手机注册/重新上线 | 引擎点亮在线状态 |

| receipt | sendTask 入库 | 引擎记账 |

| callBack | 任务终态 | 引擎更新发送状态 |

| logs | 客户发来消息(文字/图片/语音转写) | 引擎的 AI 员工据此自动回复 |

四条铁律:

1. 严禁调用 updateCallBackUrl。你一调,回调就从引擎切到你的地址,AI 员工立刻"失聪",客户消息没人回。要接入站消息,走引擎的 Agent 扩展(custom/),不要抢回调。

2. 严禁调用 forceLogout / forcedOffline(踢设备下线)。换机、解绑由平台在 xiyebot 管理后台操作。

3. 你的进程收不到 callBack,用 getTaskStatus 轮询,这是设计,不是缺失。

4. 想在 AI 员工回复之外"再发一条",先想清楚客户会不会收到两个口径。

---

6. 在本实例里怎么落地(三种模式)

模式 A:给 AI 员工加一个"主动发微信"工具(custom/<agent>/tools.ts)

适合:AI 员工在对话中决定要给某个好友/群推一条消息或图片(例如售后 AI 处理完退款,主动通知客户)。tools.ts 必须自包含(见 custom/README.md),Node 18+ 自带 fetch,直接用:

// custom/sales/tools.ts(节选)
const XIYEBOT_BASE = process.env.XIYEBOT_GEWEI_BASE || "https://flowbot.efguc.com/wx";

async function sendWechatText(robotId: string, searchText: string, message: string, idempotencyKey: string) {
  const res = await fetch(`${XIYEBOT_BASE}/api/sendTask?robotId=${encodeURIComponent(robotId)}`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ taskList: [{ type: 10001, searchText, message, idempotencyKey }] }),
    signal: AbortSignal.timeout(15000),
  });
  const json = await res.json();
  if (json.code !== 200) throw new Error(`xiyebot ${json.code}: ${json.message}`);
  return json.data.taskList[0].taskId as string;
}

export const tools = [
  {
    name: "notify_customer_wechat",
    description: "把一条通知发到客户的微信(用客户在系统里登记的微信显示名)",
    parameters: { type: "object", properties: { wechatName: { type: "string" }, text: { type: "string" } }, required: ["wechatName", "text"] },
  },
];

export async function callTool(name: string, args: any, ctx: any) {
  if (name === "notify_customer_wechat") {
    const robotId = process.env.MYT_XIYEBOT_ROBOT_ID;  // 客户在 .env 配;或从 flowbot_robots 读
    if (!robotId) return { error: "未配置 MYT_XIYEBOT_ROBOT_ID" };
    const taskId = await sendWechatText(robotId, args.wechatName, args.text, `agent-${Date.now()}-${args.wechatName}`);
    ctx.log(`xiyebot task queued ${taskId}`);
    return { ok: true, taskId, note: "已排队,手机 10~30 秒内发出" };
  }
  return { error: `未知工具 ${name}` };
}

工具的导出签名、ctx 能力、白名单以 custom/README.md 为准;上面只演示 xiyebot 调用部分。改完 pm2 restart agentos。

模式 B:独立进程做定时/批量任务(custom/wechat-jobs/)

适合:每天 10 点发朋友圈、每周一发视频号、订单事件驱动的批量通知。按 CLAUDE.md §1.5 的"四步并排法":自己的 Node 程序、自己的 pm2 进程、只读账号读平台表、自己的 myt_* 表记发送日志。

// custom/wechat-jobs/index.js(骨架)
require("dotenv").config({ path: "/opt/agentos/.env" });
const BASE = process.env.XIYEBOT_GEWEI_BASE || "https://flowbot.efguc.com/wx";
const ROBOT = process.env.MYT_XIYEBOT_ROBOT_ID;

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const jitter = (min, max) => min + Math.random() * (max - min);

async function api(path, body) {
  const res = await fetch(`${BASE}/api/${path}?robotId=${encodeURIComponent(ROBOT)}`, {
    method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body || {}),
    signal: AbortSignal.timeout(15000),
  });
  return res.json();
}

async function waitTask(taskId, timeoutMs = 120000) {
  const until = Date.now() + timeoutMs;
  await sleep(10000);
  while (Date.now() < until) {
    const r = await api("getTaskStatus", { taskId });
    if (r.code === 200 && r.data.finished) return r.data;
    await sleep(5000);
  }
  return { status: "poll_timeout", finished: false, success: false };
}

async function broadcast(names, message) {
  const info = await api("getRobotInfo");
  if (!info.data?.online) throw new Error("微信号离线,今天不发");
  for (const name of names) {
    const key = `myt-broadcast-${new Date().toISOString().slice(0, 10)}-${name}`;
    const r = await api("sendTask", { taskList: [{ type: 10001, searchText: name, message, idempotencyKey: key }] });
    const taskId = r.data?.taskList?.[0]?.taskId;
    const done = taskId ? await waitTask(taskId) : { status: "enqueue_failed", success: false };
    // TODO: INSERT INTO myt_wechat_send_log (...) VALUES (...)
    await sleep(jitter(8000, 20000));
  }
}

启动:pm2 start custom/wechat-jobs/index.js --name myt-wechat-jobs && pm2 save。定时用 node-cron 或系统 crontab 拉起一次性脚本都行。

模式 C:后台加一个页面,手动触发

在 ui-src/client 加页面 → 调你自己的 /myapp/ 后端 → 后端按模式 B 的 api() 调 xiyebot。不要在浏览器里直接调 xiyebot(跨域、授权码暴露给前端)。

---

7. 上线前检查清单

---

8. 故障排查

| 症状 | 第一动作 |

|---|---|

| 一切 401 robotId无效 | 查 flowbot_robots 里这个授权码是否存在、unboundAt 是否为空;仍不对 = 平台侧禁用/删除了授权码,找平台 |

| getRobotInfo 长期 online=false | 手机端问题:让客户看手机上 Xiyebot Agent 是否在运行、微信是否登录、网络是否通;App 打开后 30 秒内应恢复 |

| sendTask 200 但任务一直 pending | 手机离线(见上);或前面有任务卡住——看 getTaskStatus 最早那条的状态 |

| failed + 找不到联系人 | searchText 与微信显示名不一致(备注 vs 昵称、空格、emoji);让客户在手机微信搜索框试一遍 |

| failed + CHAT_MESSAGE_CONTENT_MISMATCH | 手机上没看到发出去的气泡,常见于弱网/微信弹窗;带同一 idempotencyKey 重试一次,再不行找平台看手机截图 |

| timeout | 手机在 timeoutSeconds 内没领到任务;确认在线后重发(同 idempotencyKey) |

| 图片/文件发不出 | URL 手机能不能下载(必须 https 公网,不能是内网/需登录的地址);fileName 带正确扩展名 |

| 客户收到两条回复 | 你的代码和 AI 员工都回了——检查模式 A 的工具是否在入站回复链路里被调用 |

| 朋友圈/视频号发布失败 | 微信版本更新导致页面变化,属于平台维护范围,找平台;先用 dryRun 复现 |

找平台:授权码禁用/解绑/换机、加微信号席位、手机端 App 升级、微信改版导致某类任务集体失败、需要新的任务类型(如小程序真卡片、语音气泡)。

平台联系方式:见同目录 CLAUDE.md 末尾。

---

9. 能力边界(2026-09-08)

| 能力 | 状态 |

|---|---|

| 发文字/图片/文件到好友、群 | 稳定 |

| 群 @ | 稳定 |

| 朋友圈图文(≤9 图)、dryRun | 稳定 |

| 视频号发布(videoUrl 下载入相册后发布) | 2026-07 上线,建议先单条验证 |

| 抖音发布视频(10022) | 仅发布;评论/私信/数据回读开发中 |

| 小红书发布视频(10023) | 仅发布;评论/私信/数据回读开发中 |

| 加好友、改备注、读好友资料、同步通讯录 | 可用,慢 |

| 入站文字 / 语音转文字 / 图片(占位或 URL)回调给引擎 | 稳定(引擎消费,你不接) |

| 小程序卡片(非链接文本)、微信原生语音气泡 | 未开放,需要时找平台 |

| 企业微信 | 不走 xiyebot,是另一条通道(WXWORK_*) |

---

10. 变更记录