GA4 怎么统计在线客服带来的转化:事件代码和关键事件设置
二十行 JavaScript 就能把聊天事件发进 GA4。真正难的是判断哪个事件才算转化,哪个只是看起来像。
在 GA4 里统计在线客服带来的转化,做法是:监听聊天插件的 JavaScript 事件,用 gtag('event', …) 发到 GA4,再到管理 → 数据显示 → 事件里把该算转化的那个标成关键事件。代码部分大概二十行。难的是判断哪个事件才算转化——绝大多数独立站就是在这一步统计错的。
要点速览
- 浏览器里能拿到两个信号:聊天窗口打开了、收到了一条回复。分别用
SealReply.on("open", …)和SealReply.on("message", …)监听。 - 发之前一定要去重。访客换一个页面时插件会把聊天窗口恢复成打开状态,
open会再触发一次,一个人能被算成三次转化。 - 只有在「询盘主要靠聊天进来」的站上,
chat_open才值得标成关键事件,否则留着当普通事件用来做对比。 - 浏览器永远看不到「访客真的发了一句话」,这个信号在你自己的服务器上,是
conversation.created这个 Webhook。 - 要算钱,别在 GA4 里硬凑:把订单回传给客服后台,报表 → 成果会直接告诉你「聊过天后下单」有多少单、多少钱。
哪个动作才算在线客服的转化?
在线客服的转化,指的是访客变成了一条你能跟进的询盘,而不是聊天窗口被打开的那一刻。打开聊天太便宜了:有人纯属好奇点一下,有人点开就走,有人来回点两次。把每次打开都标成关键事件,转化数会虚高,Google Ads 的智能出价也会跟着学歪。
所以写代码之前先想清楚你要量的是哪个信号。能拿到的信号一共这几个:
| 信号 | 在哪儿拿 | 适合当关键事件吗 |
|---|---|---|
| 聊天窗口被打开 | 浏览器,插件的 open 事件 |
只有询盘主要靠聊天进来的站适合 |
| 访客收到了一条回复 | 浏览器,插件的 message 事件 |
不适合,它量的是你团队的速度 |
| 访客发出了第一句话 | 服务器,conversation.created Webhook |
适合,这才是真正的询盘 |
| 访客留下了邮箱或电话 | 服务器,visitor.identified Webhook |
适合,这是一条线索 |
| 聊天之后下了单 | 服务器,订单回传接口 | 适合,但在客服后台看,别在 GA4 里看 |

浏览器只知道前两行。这不是某一家插件的毛病,是所有聊天插件的共同边界:页面能看见窗口开没开、回复到没到,看不见访客打了什么字。所以大多数小站实际可行的方案是——浏览器端发 chat_open,把它当成一个参与度信号;等开了付费套餐,再让服务器把精确的转化补上。
第一步:先确认页面上已经有 GA4
打开自己的网站,按 F12,在控制台里敲 typeof gtag。返回 "function" 就可以往下做了。返回 "undefined",说明要么没装 GA4,要么是用 Google 跟踪代码管理器(GTM)装的,直接看文末的 GTM 的做法。
另外要准备好衡量 ID,就是 G- 开头加十位字符的那串,在管理 → 数据流 → 你的网站数据流里。用 gtag 直接装的话,页面上长这样:
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag("js", new Date());
gtag("config", "G-XXXXXXXXXX");
</script>
两个细节会影响下一步。一是 gtag 本质上是个队列,Google 的脚本还没加载完就调用它也没事,之后会补发;二是聊天插件的脚本和它互不相干,两段代码谁先谁后都行。
第二步:把聊天事件发到 GA4
把下面这段贴在两段代码后面、</body> 之前。它会等插件就绪,然后转发两个事件,并且保证一个浏览器会话里每个事件只发一次:
<script>
function whenChatReady(fn) {
if (window.SealReply) fn();
else window.addEventListener("sealreply:ready", fn, { once: true });
}
// GA4 来一条算一条:同一个访客在四个页面上打开聊天,
// 就是四次转化。所以每个事件一个会话只发一次。
function sendOnce(name, params) {
var key = "sr_ga4_" + name;
if (sessionStorage.getItem(key)) return;
sessionStorage.setItem(key, "1");
gtag("event", name, params);
}
whenChatReady(function () {
SealReply.on("open", function () {
sendOnce("chat_open", { chat_page: location.pathname });
});
SealReply.on("message", function () {
sendOnce("chat_reply_received", { chat_page: location.pathname });
});
});
</script>
三段各自在干什么:
whenChatReady解决的是widget.js带async加载的问题:你的脚本跑起来的时候,window.SealReply可能还不存在。插件就绪时会在window上触发一个sealreply:ready事件,所以要么发现它已经加载好了,要么等这一下。不加这个判断,网速慢的时候监听没挂上、网速快的时候挂上了,或者反过来,而且不报错。sendOnce把标记写进sessionStorage,它跨页面跳转还在,关掉标签页或重开浏览器就没了。GA4 自己的会话是「30 分钟无活动就算结束」,两者对不齐,但实际用起来差别不大:访客明天再来,在两边都算新会话。chat_page记录访客是从哪个页面打开聊天的。GA4 本来就给每个事件带page_location,但自己定义的参数在探索报告里更好用,而且能直接看出是价格页还是产品页在制造疑问。
`open` 触发的次数比点击多
访客翻到下一页时,插件会把聊天窗口恢复成打开状态,这一下也会触发 open,没有任何点击。你自己的代码调 SealReply.open() 时同样会触发。所以 sendOnce 不是锦上添花:少了它,一个开着聊天看了两页的访客,就是两次 chat_open。
还有几条 Google 那边的硬限制,起名字之前先知道为好:事件名最长 40 个字符,一个事件最多带 25 个参数,参数值超过 100 个字符会被截断。这些写在 GA4 的资源项限制里,其中事件名超过 40 个字符的,根本不会被当成关键事件统计。
单页应用:监听只挂一次
用 React、Vue 或者 Next.js 的站,上面这段要放在根组件里跑一次,不要放在渲染「联系我们」按钮的那个页面组件里。前端路由跳转不会重新加载 widget.js,所以挂在页面组件里的监听会在每次切路由时再挂一遍,事件成倍往外发。要取消监听,就把函数存进变量,传给 SealReply.off("open", handler)。路由这块的细节在 Next.js 加聊天插件里写了。
第三步:把事件标成关键事件
GA4 里的关键事件就是以前说的「转化」:你标记为有价值的事件,之后才能进转化报表、受众群体和 Google Ads 出价。标记只要点四下,但 GA4 得先收到过这个事件。
- 自己先触发一次——在自己网站上把聊天打开。
- 进 GA4,点管理 → 数据显示 → 事件,
chat_open一天之内会出现在列表里。 - 打开它那一行的标记为关键事件开关。Google 的创建和修改事件这篇讲的是同一个开关。
- 默认关键事件价值先留空,除非你确实知道一次咨询平均值多少钱。随手填的数字会顺着所有带收入的报表一路扩散出去。

**别把 chat_reply_received 标成关键事件。**它是你团队回复时才触发的,意味着你回得快转化就涨、你去吃饭转化就跌——这是一个关于排班的指标,套着一个关于访客的指标的外衣。留着当普通事件,第五步会用上它。
第四步:验证事件真的发出去了
用报表 → 实时:在自己网站上打开聊天,等最多一分钟,在事件计数卡片里找 chat_open。实时报表看的是最近 30 分钟,而常规报表可能要等一天,所以测试只能在实时里测。
要看细节——参数到没到、值是什么——就开 DebugView。在页面网址后面加 ?debug_mode=1,或者临时在 gtag("config", …) 里加一个 debug_mode: true,然后一边点一边看管理 → DebugView。每个事件都会变成一张卡片,参数列在下面,几秒内就出现。
什么都没出现的时候
先看浏览器控制台有没有报错:你的监听函数抛异常会被插件吞掉(这样写错代码不会把聊天搞挂),代价就是出了错也静悄悄的。然后在那个具体页面上再确认一次 typeof gtag === "function"——有些建站平台的「统计」开关只在首页加载代码。
第五步:聊过天的访客,下单率更高吗?
chat_open 跑起来之后,就能回答你当初装这套东西想回答的问题了:聊过天的人,是不是比没聊过的更容易下单?用对比,别去分别看两张报表。
- 打开任意一张常规报表,比如报表 → 参与度 → 网页和屏幕。
- 点添加对比 → 新建,条件设成事件名称完全匹配
chat_open,范围选用户。 - 再加一个条件相反的对比,这样「聊过天」和「没聊过」就并排放着了。
- 把报表的指标换成
purchase,或者你站上本来就在发的那个关键事件。
结果要会读。会去点开聊天的访客,本来就比不点的更有意向——在聊天起作用之前,他已经走得更远了。这个对比能告诉你:愿意聊天的这拨人有多大、有多活跃,这个数字在你决定「要不要多排几个小时的班」时很有用。它不能证明是聊天带来了这些订单,而且 GA4 再怎么配也证明不了。
要算钱的话,把订单回传回来
把钱归到某次聊天上,是服务端的活,客服后台已经做好了。你的服务器把每笔订单发给订单接口,同一个访客在聊天后 30 天内下的单,会挂到他最近一次会话上:
POST https://api.sealreply.com/v1/site-orders
Authorization: Bearer sr_live_…
Content-Type: application/json
{
"site_id": "0192f000-0000-7000-8000-000000000201",
"order_id": "A-1001",
"amount": "59.90",
"currency": "USD",
"external_id": "42"
}
external_id 是你自己网站的用户 ID,和传给插件的那个是同一个,所以这需要先打开签名识别——做法在在线客服怎么识别登录用户里。没登录就下单的游客,订单会存下来,但不会算成「聊过天后下单」。同一个 order_id 再发一次是更新(比如改金额),不会重复计数;订单取消或全额退款,用 DELETE /v1/site-orders/A-1001 撤回,报表里就不再算它。
然后报表 → 成果里会多一张卡片「聊过天后下单(网站回传)」,显示单数和各币种金额,就放在团队手动标记的成交旁边。卡片底下那句说明是实话:访客下单前 30 天内聊过天,不代表这笔收入是聊天带来的。
这部分要付费套餐
订单回传接口和 Webhook 需要入门版及以上;免费版没有开放 API,入门版每月 10,000 次调用。第一到第五步全在浏览器里跑,所有套餐都能用,免费版也行。
如果你非要在 GA4 里拿到那个精确的转化,服务端的路子是 Measurement Protocol:在浏览器里用 gtag("get", "G-XXXXXXXXXX", "client_id", cb) 取到访客的 GA4 客户端 ID,跟着访客一起存下来,等 conversation.created 这个 Webhook 到了,再带着这个 ID 往 Google 发一个 generate_lead 事件。收 Webhook 之前先验签,代码在 Webhook 签名怎么验证里。
哪些东西绝对不能发给 Google
聊天内容、邮箱、电话、姓名,一个都不要发进 GA4。Google 的条款禁止往 Analytics 里传能识别个人的信息,而聊天记录大概是你网站上最敏感的东西:订单号、收货地址、投诉,全在里面。事件参数是用来记「形状」的,不是记「内容」的——哪个页面、什么套餐档位、聊之前看了几页。
落到操作上就是一句话:参数只从你自己的页面结构里取,不要从聊天里取。chat_page: "/pricing" 没问题;first_message: "我的订单 A-1001 还没到" 就是一次数据泄露,只是绕了个弯。SealReply 这边,聊天内容是加密存储的,每个客户一把独立密钥,平台要查看须两人批准并通知你;把同样的文字抄一份送进第三方统计工具,前面这些就全白做了。
用 GTM 的话怎么做
如果 GA4 是通过 Google 跟踪代码管理器装的,结构不变,只改最后一行:不调 gtag,改成往数据层里推。
window.dataLayer = window.dataLayer || [];
dataLayer.push({ event: "chat_open", chat_page: location.pathname });
然后在 GTM 里:用一个自定义 HTML 代码,触发条件选初始化 - 所有页面,里面放监听代码;建一个自定义事件触发器匹配 chat_open;再建一个 GA4 事件代码把它发出去,chat_page 用数据层变量取。如果你改不了网站的 HTML,或者统计是别人在管、你只想给对方一个事件,走这条路更合适。
常见问题
不用 GTM 能在 GA4 里统计在线客服吗?
能。插件直接在页面上暴露了 SealReply.on("open", handler),所以一段调用 gtag("event", "chat_open") 的 <script> 就够了。只有在你本来就在用 GTM,或者改不了网站 HTML 的情况下,多加这一层才划算。
「打开聊天」该不该在 GA4 里标成转化?
只有在询盘主要靠聊天进来的站上才该标。打开聊天窗口只需要一次好奇的点击,如果你站上大部分人不问就直接买了,把它标成关键事件会让转化数虚高,还会把噪音喂给 Google Ads 出价。更稳的关键事件是「访客留下了联系方式」,它在服务器上是 visitor.identified 这个 Webhook。
chat_open 的数字为什么高得不正常?
基本都是没去重。访客翻到下一页时插件会把聊天窗口恢复成打开状态,open 会在没有点击的情况下再触发一次,而 GA4 收到一条就算一条。上面那个 sendOnce 把标记存在 sessionStorage 里,保证每个浏览器会话只发一次。
能看出一次咨询是哪个广告系列带来的吗?
GA4 里能:chat_open 和其他事件一样继承所在会话的来源、媒介和广告系列,所以在报表 → 获取客户 → 流量获取里加上聊天对比就能看。客服后台也能看,而且不用配任何东西——报表 → 来源分析里列了这些会话背后的来源渠道、广告系列、国家和设备。
加这段代码会拖慢网站吗?
影响小到测不出来。你加的是一个监听,外加每个会话最多两次 gtag 调用,页面上的重量还是原来那两段脚本。聊天插件本身约 38 KB 的异步脚本,不阻塞页面显示,GA4 的代码同样是异步加载的。
免费版能统计吗?
能,浏览器里的部分所有套餐都能用:插件的 JavaScript 接口、open 和 message 事件、GA4 的关键事件,都不限套餐。需要入门版及以上的是服务端那半边——Webhook、开放 API,以及把订单挂到会话上的订单回传。
接下来做什么?
先把统计做实,再想做花。把 chat_open 发出去,盯两周,看看到底有百分之几的访客会去点聊天;不少小站看到这个数字时,都会往某个方向惊讶一下。
如果你站上的聊天还是装完就没动过的默认状态,最影响这组数字的两个设置是「分页面的欢迎语」和「没人在线时的接法」,网站怎么添加在线客服里都写了;从安装到接待再到跟进成交的整条链路,在小网站在线客服完整指南里串了一遍。