飞书与腾讯会议API对接实战:自动化创建会议与数据回填全指南
2026/9/15 20:16:40 网站建设 项目流程

1. 为什么要把飞书和腾讯会议打通

1.1 我遇到的真实场景

先说一下我自己碰到的实际场景。团队日常用的是飞书,日历、审批、文档、机器人全在飞书里跑,但合作方统一用的是腾讯会议。每周开对接会之前,行政的小姑娘都要先把会议时间整理成表格,然后去腾讯会议客户端手动创建一个会议,再把会议号和入会链接复制回来,一个一个填到飞书群里。遇到会议多的时候,光这一步就要折腾大半天,还经常出现会议链接发错、时间对不上的情况。

后来我接到一个任务,把这个流程自动化掉:在飞书表格里填一行需求,机器人自动帮你在腾讯会议后台创建会议,再把腾讯会议生成的会议号、入会链接、密码这些信息自动回填到表格里,同时推送到群里。做完之后,行政部门的效率提升非常明显,原来半小时的重复劳动变成了几秒钟的自动响应。

这个场景其实非常有代表性。飞书负责的是"协作入口",腾讯会议负责的是"视频会议承载",两家产品都有自己的 API 和生态,但默认互相不通。打通它们,本质上就是做一次典型的跨平台系统集成。

1.2 对接到底解决什么问题

说得直白一点,这个对接要解决三个层次的问题:

第一层是数据打通。飞书表格里存的是业务数据(会议主题、时间、参与人),腾讯会议 API 需要的是这些数据去创建一场真实的会议。两边数据的字段要能对得上,比如时间格式、时区、参会人标识,都需要做转换。

第二层是流程闭环。不是简单地创建完会议就结束了,而是要把腾讯会议返回的结果再反向写回飞书,让使用者在飞书侧就能看到完整的信息,不需要两套系统来回切换。

第三层是消息联动。创建完成后,还要通过飞书机器人把会议信息主动推送到群里,甚至支持用户在卡片上点击按钮完成操作,这就涉及消息卡片的交互逻辑。

搞清楚这三个层次,就不会把对接想成"调一个 API 就完事"这么简单了。实际上,配置权限、处理回调、设计错误处理,每一步都有不少坑,后面我会一个个展开讲。

2. 整体思路与方案选型

2.1 两条技术路径的对比

飞书和腾讯会议对接,方案上其实有几种走法,我做过一轮对比之后才确定下来。

第一种是完全靠人工 + 半自动化。用飞书的自动化流程助手(类似多维表格的自动化)去触发 Webhook 调用腾讯会议 API。这个方案上手快,不写代码也能做,但灵活性差。腾讯会议的创建接口需要鉴权、需要处理返回值,自动化流程助手处理不了复杂的逻辑,尤其是分步回填、异常重试这些场景,基本是死路。只适合做最简单的"发条消息提醒"。

第二种是用低代码平台或集成工具。比如用飞书集成平台或者第三方 iPaaS,把飞书和腾讯会议连接起来。好处是可视化配置,不需要自己维护服务器。但坏处是:腾讯会议企业 API 往往需要自定义鉴权头和动态参数,低代码平台对这类接口的支持有时候不够及时;而且一旦涉及复杂业务规则(比如根据会议类型选择不同的虚拟会议室、根据参会人数调整会议时长),低代码平台的表达力就显得捉襟见肘。

第三种是自建后端服务。在云服务器上部署一个服务,同时对接飞书开放平台 API 和腾讯会议开放平台 API,自己控制所有逻辑。这个方案最灵活,什么问题都能自己处理,代价是要写代码、要维护服务、要处理凭证管理。

我最终选的是第三种,自建一个轻量后端服务。原因很简单:这个场景涉及两个系统之间的多次调用、数据回填和消息推送,中间的逻辑用代码写最直白,排查问题也方便。而且腾讯会议的 API 鉴权采用的是 JWT 或者 OAuth2.0,这种动态签名的逻辑,用代码处理比在低代码平台上到处找组件要可靠得多。

2.2 我最终选择的架构

服务端的选型,我用的是 Python + FastAPI,部署在云服务器上,进程用 systemd 管理。选择 FastAPI 是因为它写起来轻快,异步支持好,处理飞书的回调验证(Challenge 校验)非常方便,而且自动生成接口文档,调试的时候直接打开/docs就能测试接口。

整个架构跑起来是这样的:

  • 飞书侧创建企业自建应用,开通机器人能力,配置事件订阅和权限。
  • 腾讯会议侧申请企业开发者权限,拿到一个secret_idsecret_key,用于生成 API 调用的鉴权签名。
  • 后端服务提供三个核心接口:接收飞书事件回调、接收飞书卡片回调、给飞书表格回填数据。
  • 数据存储我当时用的是 MySQL,但后来发现其实用 Redis 或者 SQLite 也足够。核心要存的数据就两类:会议需求记录、腾讯会议创建的会议结果。在并发不高的场景下,SQLite 完全够用,还省去维护数据库的麻烦。

我可以把架构简化成一句话:飞书侧是入口和出口,腾讯会议侧是能力提供方,中间的业务服务负责翻译和编排

3. 前期准备:应用创建与权限配置

3.1 飞书侧准备

飞书这边的准备工作,我踩了不少坑,这里按正确顺序捋一遍。

第一步,进入飞书开放平台,创建企业自建应用。注意一定要选"企业自建应用",不是"商店应用"。创建完之后,你会得到一个App IDApp Secret,这两个值相当于应用的账号密码,后面获取tenant_access_token要用。

第二步,给应用开启机器人能力。在应用详情页的"添加应用能力"里找到"机器人",开启后应用就拥有了一个机器人身份,可以出现在群聊里发消息。

第三步,配置权限。飞书的权限模型是按 API 粒度开的,不是安一次全部授权。我们这场景需要开通的权限有这么几项:

  • im:message:发送消息
  • im:message.group_at_msg:接收群里 @机器人的消息(如果要做关键词触发)
  • docs:doc:创建和编辑云文档
  • bitable:app:读写多维表格
  • contact:user.base:readonly:读取用户基本信息(用于把飞书用户 ID 转成参会人)

权限开好之后,要发布版本。飞书开放平台的应用有一个版本发布机制,修改配置之后必须先创建版本、申请发布(如果是自建应用,管理员审核通过或者直接可用),新配置才会生效。这个坑我一开始不知道,改了半天权限,线上根本不生效,白白浪费了两个小时。

第四步,配置事件订阅。如果要支持"用户发一条消息给机器人就触发会议创建"这种交互,就需要在"事件与回调"里订阅im.message.receive_v1事件,然后把后端服务的回调地址填进去。飞书验证回调地址的方式是发送一个 Challenge 请求,你的服务需要正确返回challenge字段,否则校验不通过。

3.2 腾讯会议侧准备

腾讯会议的 API 和飞书不太一样,它把接口能力分成了企业版 API个人版 API,很多限制也略有不同。

我这边申请的是企业版 API。流程是:进入腾讯会议开放平台,注册成为开发者,创建应用,然后提交企业认证。审核通过之后,会拿到一串secret_idsecret_key

这里有个很重要的点:腾讯会议 API 的调用方式比较特殊,不是简简单单带上 token 就能请求。它的鉴权机制是,把secret_idsecret_key以及请求参数组装成一个 JWT(JSON Web Token),然后放在请求头的X-TC-Key字段里。简单说,你每次调用接口前,都要先用 secret_key 签一个短时效的 JWT

腾讯会议的 API 还有一层概念,叫OAuth2.0 授权。如果要用某个用户身份去创建会议(比如以某个员工的账号创建会议),需要该用户先授权你的应用。如果是企业版管理员统一配置,有一种更简单的处理方式:使用企业管理员身份创建一个"会议用户",然后所有会议都用这个账号统一创建。这种方式在内部工具场景下比较常见,也省去了逐个用户的 OAuth 授权流程。

还有一个很容易被忽略的点:腾讯会议要求服务出口 IP 加白名单。如果你的服务器 IP 不固定,每次部署都要去开放平台后台更新白名单,否则 API 返回的会是鉴权错误而不是接口不存在。上线之前一定先确认好服务器公网 IP,并在开放平台后台配置好。

4. 核心实现:从表格到会议再到回填

4.1 第一步:飞书机器人接收表格和触发指令

我做的这个方案里,飞书机器人承担了两个职责:一是接收用户发来的消息(比如"创建下午3点的会议"),二是把会议结果主动推送到群里。

先看如何让机器人收到指令。飞书的事件订阅会在用户 @机器人 或单独给机器人发消息时,把消息内容推送到你的回调地址。回调解密之后,大致长这样:

{ "schema": "2.0", "header": { "event_type": "im.message.receive_v1", "event_id": "xxxxxx" }, "event": { "message": { "message_id": "om_xxxx", "content": "{\"text\":\"创建下午3点的会议\"}", "chat_id": "oc_xxxx", "message_type": "text" }, "sender": { "sender_id": { "open_id": "ou_xxxx" } } } }

注意content字段是字符串,里面套了一层 JSON,需要自己解析出来。我在写代码时用的是飞书官方 Python SDK,它可以直接帮你完成签名验证、事件解析的步骤,省了很多事。但如果你用的是 Java 或者 Go,飞书官方也有对应 SDK,尽量别自己实现签名算法,容易在细节上翻车。

那"发送表格"的需求怎么处理?要分清楚是哪种表格:

  • 如果是要把一段结构化数据以表格形式展示给用户,可以用消息卡片table结构,或者直接用 Markdown 文本画一个简单的 ASCII 表格。
  • 如果是要求生成一个真正的 Excel 文件发送出去,那就需要先在一个临时目录生成.xlsx文件,然后调用飞书的上传文件接口,把文件上传到飞书的 IM 系统,再通过消息接口把文件消息发出去。

我在实际场景里两种都做了。日常的会议列表用卡片表格展示,需要留档的数据生成 Excel 发到群里。上传文件的代码用 Python 的openpyxl生成 Excel,然后调飞书接口上传,核心代码如下:

import openpyxl import requests def send_excel_to_feishu(chat_id: str, rows: list, tenant_access_token: str): wb = openpyxl.Workbook() ws = wb.active ws.title = "会议列表" ws.append(["会议主题", "会议时间", "会议号", "入会链接"]) for row in rows: ws.append(list(row)) local_path = "/tmp/meetings.xlsx" wb.save(local_path) with open(local_path, "rb") as f: resp = requests.post( "https://open.feishu.cn/open-apis/im/v1/files", headers={"Authorization": f"Bearer {tenant_access_token}"}, data={"file_type": "xlsx", "file_name": "meetings.xlsx"}, files={"file": f}, ) file_key = resp.json()["data"]["file_key"] requests.post( "https://open.feishu.cn/open-apis/im/v1/messages", headers={"Authorization": f"Bearer {tenant_access_token}"}, params={"receive_id_type": "chat_id"}, json={ "receive_id": chat_id, "msg_type": "file", "content": json.dumps({"file_key": file_key}), }, )

这里有个小细节:飞书上传文件接口要求file这个字段是真实的文件对象,不能用绝对路径字符串代替,很多人在这一步报"文件不存在"的错误,实际上是因为传给接口的是路径而不是文件句柄。

4.2 第二步:调用腾讯会议 API 创建会议

腾讯会议的接口调用是整个对接里最核心的一环。这里我用 Python 的requests库直接调 HTTP 接口,鉴权方式用 JWT。

先看 JWT 怎么生成。腾讯会议的鉴权规则大致是这样的:用secret_id作为 JWT 的appidsecret_key作为签名密钥,iat是当前时间戳,exp是过期时间(一般设置 10 分钟以内),然后附带一个random字段防止重放。

生成 JWT 的代码我贴一下:

import time import random import jwt def generate_jwt(secret_id: str, secret_key: str) -> str: now = int(time.time()) payload = { "appid": secret_id, "iat": now, "exp": now + 600, "random": random.randint(10**15, 10**16 - 1), } token = jwt.encode(payload, secret_key, algorithm="HS256") return token

生成完 JWT 之后,调用创建会议的接口。腾讯会议的接口路径是POST https://api.meeting.qq.com/v1/meetings,请求头要带上X-TC-Key,值为 JWT,同时带上X-TC-Nonce(一个随机字符串)、X-TC-Timestamp(当前时间戳),以及Content-Type: application/json

创建会议的请求体关键字段是这样的:

{ "userid": "meeting_robot_001", "subject": "周度项目进度同步会", "type": 0, "start_time": "1710000000", "end_time": "1710007200", "settings": { "mute_enable_join": true, "allow_unmute_self": false, "join_approval": true } }

解释一下几个容易踩坑的字段:

  • type是会议类型,0 表示一次性会议,1 表示周期性会议。如果是周期会议,还需要额外传recurrence_rule参数。
  • start_timeend_time秒级时间戳,不是毫秒。这个特别容易出错,飞书 API 用的是毫秒,腾讯会议这里用秒,两个系统对接的时候一定要统一转成秒。我第一次调的时候直接把飞书给的毫秒时间戳传过去,结果会议开始时间变成了 1970 年附近。
  • userid不是随便写的,必须是腾讯会议系统里真实存在的用户 ID。我当时是用企业管理员在腾讯会议后台建了一个专用账号"会议机器人",企业版 API 允许用这个账号身份去创建会议室。
  • settings里的mute_enable_join控制入会是否静音,allow_unmute_self控制参会人能否自行解除静音,join_approval控制是否开启入会审批。这些配置要根据业务需求来定,如果是全员大会,建议开启静音入会;如果是项目讨论会,就保持放开。

调用成功的返回值里,有几个字段是我们做回填必须拿到的:meeting_id(会议唯一标识)、meeting_code(9 位数的会议号,用户在腾讯会议客户端输入这个加入会议)、join_url(一键入会链接)。这三个字段要原样存下来,后面要写回飞书。

4.3 第三步:把会议信息回填到飞书表格

会议创建成功之后,接下来就是把结果回填到飞书的多维表格或者云文档里。

如果用的是飞书多维表格(Bitable),回填操作就非常方便,直接调用新增记录的接口,把会议号、链接、时间写进去。多维表格的接口路径是POST /open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records

records = { "fields": { "会议主题": "周度项目进度同步会", "会议时间": "2024-03-10 15:00", "会议号": "123456789", "入会链接": "https://meeting.tencent.com/dm/xxxx", } } headers = { "Authorization": f"Bearer {tenant_access_token}", "Content-Type": "application/json", } resp = requests.post( "https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records", headers=headers, json=records, )

这里有个细节:多维表格的字段类型有多种,如果字段类型是"文本",直接传字符串没问题;但如果字段类型是"日期",就需要传时间戳,并且要感知字段类型。一开始我在表格里把"会议时间"字段设置成了日期类型,但代码里传的是字符串,接口虽然返回 200,但表格里显示为空。排查了半天发现是字段类型不匹配。

如果用云文档(Docx),回填方式就是追加一段文字或者插入一个表格。追加内容的接口是POST /open-apis/docx/v1/documents/{document_id}/blocks/{block_id}/children,需要先定位到要插入的位置,然后把段落或者表格块作为 children 传进去。相比多维表格,操作复杂一些,但如果业务流程本身就是在文档里记录会议纪要,那这种方式更合适。

4.4 消息卡片与回调交互

除了回填表格,我还在群里加了一个消息卡片,用来展示会议创建结果,并且支持用户直接在卡片上确认或者取消。这就涉及飞书的交互卡片

消息卡片用 JSON 定义,可以在飞书开放平台的"卡片搭建工具"里可视化编辑,然后把生成的 JSON 直接复制到代码里。我用的卡片大致结构是这样:

{ "config": { "wide_screen_mode": true }, "elements": [ { "tag": "div", "text": { "tag": "lark_md", "content": "**会议已创建成功**\n会议主题:周度项目进度同步会" } }, { "tag": "div", "fields": [ {"is_short": true, "text": {"tag": "lark_md", "content": "**会议号**\n123456789"}}, {"is_short": true, "text": {"tag": "lark_md", "content": "**入会链接**\n[点击入会](https://meeting.tencent.com/dm/xxxx)"}} ] }, { "tag": "action", "actions": [ {"tag": "button", "text": {"tag": "lark_md", "content": "发送入会提醒"}, "value": {"action": "send_reminder", "meeting_id": "xxxx"}}, {"tag": "button", "text": {"tag": "lark_md", "content": "取消会议"}, "type": "danger", "value": {"action": "cancel_meeting", "meeting_id": "xxxx"}} ] } ] }

这个卡片发到群里之后,用户点击按钮,飞书会把一个回调请求 POST 到你的服务端。回调里带着你在value字段里定义的数据(比如meeting_idaction)。你的服务解析这些参数,再决定下一步做什么。

比如点击"取消会议",服务端就去调用腾讯会议的取消会议接口(DELETE /v1/meetings/{meeting_id}),然后更新群里的卡片,把状态改成"已取消"。这里要注意,腾讯会议的取消会议接口可能需要传入该会议的创建者userid,所以设计表结构的时候要把创建关系存下来,不能只存会议号。

5. 常见问题与踩坑记录

5.1 常见问题速查表

这部分我整理了对账时候最常遇到的问题,方便你排查时直接对照。

问题现象可能原因解决方法
飞书回调验证不通过Challenge 返回格式不对,或者没有返回原始 challenge 字段确保回调响应体是{"challenge": "xxx"},且 Content-Type 是application/json
调用飞书 API 报 10003 权限错误应用未发布或权限未开通检查应用版本是否发布,权限列表里是否勾选对应 API 权限
腾讯会议 API 返回 401JWT 签名错误或服务 IP 不在白名单检查 secret_id/secret_key 是否正确,确认服务器出口 IP 已加入白名单
会议时间不对时间戳单位没统一腾讯会议接口用秒级时间戳,飞书接口用毫秒级,做好单位换算
飞书机器人发不出文件上传文件时传了文件路径而不是文件句柄open()打开文件后再传入请求
多维表格回填后字段为空字段类型不匹配,比如日期字段传了字符串先查字段类型定义,按类型传对应格式的数据
卡片按钮点击没反应没有配置卡片回调地址,或者回调返回超时在飞书开放平台配置请求网址,回调接口在 3 秒内返回
腾讯会议 API 返回参数错误必填字段缺失,比如userid不存在确认企业后台已经创建了对应的会议用户账号

5.2 我踩过的几个坑

第一个大坑是飞书回调的加密机制。飞书开放平台在事件订阅里有一个"Encrypt Key"和"Verification Token"的设置。如果开了加密,所有回调请求体里的encrypt字段都是密文,需要先用 AES 解密才能拿到 JSON。我当时一开始没注意到这个开关,收到回调之后直接解析event字段,结果永远是空的,排查了很久才发现是加密问题。如果不想自己处理加解密,可以直接在开放平台把这个开关关掉,不过生产环境建议还是开着,安全性更好。

第二个坑是腾讯会议的限流策略。腾讯会议企业 API 对单账号的调用频率是有限制的,一般是每秒钟几次。如果你在回调逻辑里把"创建会议 + 回填表格 + 发卡片"串行都做了,一旦并发一上来,很容易触发限流。解决办法是做一个简单的队列,把请求异步消化掉,或者在调用之间加一点延迟。

第三个坑是时间戳和时区。飞书多维表格的日期字段,如果不指定时区,默认是 UTC 存储的;而腾讯会议创建会议传的是 UTC 时间戳。这里要做统一处理,我建议所有内部逻辑都用 UTC 时间戳,只在展示给用户的环节转成北京时间。否则会出现用户在飞书看到的时间比实际会议时间早了 8 个小时的诡异问题。

第四个坑和 "腾讯会议不能使用电脑自带摄像头吗" 这个热搜词有点关联。有同事反馈,通过 API 创建会议然后从腾讯会议客户端入会时,摄像头检测不到,问是不是 API 创建的有问题。其实不是,这是腾讯会议客户端的本地设备设置问题,跟 API 无关。API 创建会议和客户端手动创建会议没有任何差别,设备检测是客户端本地功能。排查思路是检查系统权限(比如 macOS 需要给腾讯会议摄像头权限)、检查是否有其他程序占用了摄像头、检查腾讯会议设置里的摄像头选择。这个问题虽然无关 API 本身,但因为是高频问题,我在交付文档里也专门写了排查步骤。

第五个坑是消息卡片更新的幂等性。飞书的卡片消息支持更新,但更新接口要求传message_id。如果你把卡片消息发出去之后没有保存message_id,后面想更新就找不到了。我的做法是在数据库里把message_idmeeting_id关联起来存好,做状态流转时按meeting_id查出来更新。

6. 一点扩展思路

这个项目做完之后,我发现"飞书 - 腾讯会议对接"其实只是一个起点,同类的对接需求还有很多可以做。

比如,飞书日历已经支持创建带视频会议的日程项,但目前只支持飞书自家的视频会议,不能直接绑定腾讯会议。你可以做一个反向逻辑:用户创建飞书日程 → 钩子捕获 → 自动创建腾讯会议 → 把腾讯会议的入会链接写回日程描述里。这样用户在飞书日历看到的还是一个普通日程,但点进去就带腾讯会议链接,体验上几乎是无感的。

再比如,用飞书多维表格做一个"会议室预约系统"。表格里每个记录代表一个时间段,用户预约后自动创建腾讯会议,在记录里标记"已预约",到期前自动发送提醒。这就是把业务系统、消息系统和会议系统三者串起来,价值会比单一的会议创建大很多。

还有,如果要接 AI 能力,可以在飞书机器人里接一个大模型,用户用自然语言描述会议需求(比如"周四下午和客户开个方案评审会,预计一小时"),机器人通过大模型解析出会议主题、时间、参与人,然后自动走底层的创建逻辑。这个方向现在很热,如果把这个能力做出来,体验会再上一个台阶。我在调研过程中看到很多人在做类似的自然语言转会议的操作,技术实现上并不复杂,核心就是把飞书机器人收到文本发给大模型接口,让模型输出结构化 JSON,再传给腾讯会议创建接口。

说回这个项目的本质,它不是一个"高精尖"的东西,但它是很典型的企业级系统集成实践:两个独立平台都有完善的开放 API,但默认不互通,中间需要一个服务把它们串起来。这个串的过程,考验的不是单一平台 API 用得多熟,而是对数据流、权限模型、错误处理的整体把控能力。

我个人在做完这个项目之后,最大的体会是:对接类项目,看起来是写几个接口,但实际上 60% 的时间都花在排查"两边定义不一致"的问题上。时间戳单位、字段命名、权限范围、回调格式,每一个小差异都可能导致莫名其妙的 bug。所以如果让我给后来者一条建议,那就是开工之前先把两边 API 文档的常用字段和数据类型做成一张对照表,贴在代码仓库里,所有开发讨论都以这张表为准。这个前期投入,能帮你在后面的联调阶段省下至少一半的时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询