Skip to content

传客户资料给客服 ​

客服看到的不再是「匿名访客 #241」,而是带姓名、手机号、会员等级的真实客户。同一个人换设备、隔几天再来咨询,也会归并到同一份客户档案里。

传过去的资料显示在客服工作台对话右栏的客户档案里,客户自己看不到:

客服工作台对话右栏的客户档案,含基本资料、标签、业务字段与备注

什么时候需要这一页 ​

  • 你的网站有登录体系,希望客服接待时就知道对方是谁
  • 想把会员等级、累计消费、所属门店这类业务信息带给客服

纯展示型官网没有登录体系的话,这一页可以跳过。

不传身份时,合从靠存在客户浏览器里的一个本地标识认人。客户清理浏览器数据、换设备、换浏览器或者用无痕模式,这个标识就没了,回来会被当成新访客,之前聊过的内容他自己也看不到(客服那边的历史对话始终完整保留)。传了客户 ID 之后按 ID 认人,换手机换电脑都能接上同一段历史。

在哪里调 ​

在接入代码之后加一段,只要客户是登录状态就调一次:

html
<script>
  window.hecong = window.hecong || function (c) { (window.hecong.q = window.hecong.q || []).push(c) }
  window.hecong(function (hc) {
    hc.identify({
      id: 'c9a1f0e2-7b34-4d58-9f61-0ab2d3c4e5f6',
      profile: { name: '张三', phone: '13800138000' },
      data: { member_level: 'gold' }
    }).catch(function (e) {
      // 失败时客户仍是匿名访客,客服照常能接待。
      // 接进你自己的日志,否则线上会出现「时有时无的匿名客户」且难以复现
      console.error('identify failed', e)
    })
  })
</script>

示例第一行的占位函数保证这段代码早于接入脚本执行也不报错,照原样复制即可,作用见接入代码与配置项。

访客点开聊天窗之后,客服工作台右侧的客户资料区就会显示这些信息。访客一直没点开的话,这次调用会先存下来,资料不会发出去 —— 自测时先把聊天窗点开,再看工作台。

客户首次咨询时是匿名的,调 identify() 之后,之前的匿名对话会自动合并到这个客户名下。

客户 ID 怎么选 ​

id 是这套机制的地基,选错了会出真问题。合从按它认人,两个客户拿到同一个 id,系统就认为他们是同一个人,后来的那位会直接看到前一位的聊天记录。

三条要求:

  1. 每个客户不一样。拿不到客户 ID 的时候不要传固定值凑数,整个 identify() 不调即可,那样就是匿名访客,客服照常接待。guest、test、0、undefined、null 这类一看就是占位的值会被直接拒绝,这位客户不会有会员身份。要当场确认是不是撞了这一条,在网址后面加 ?hcdebug=1 打开调试模式;要在代码里判,请求返回 400,错误标识是 web_sdk_identify_user_id_placeholder。
  2. 不会变,各处也要一模一样。客户改昵称、换手机号之后还是同一个 id,否则跨设备认不出、历史对话接不上。多个页面或多个渠道各自拼这个值时,大小写、前后空格、有没有加前缀都要对齐 —— 差一个字符就是另一个人。
  3. 别人猜不到。

用连续数字会让客户看到别人的聊天记录

合从的后端不校验签名,id 的取值就是唯一的一道防线。用了 1001、1002 这种自增数字,任何人把值改成 1003 就能进到别人的对话里。

可以用:数据库 UUID、32 到 36 位随机串、md5(你的内部用户 ID + 你自己的盐)

不能用:自增数字、手机号、邮箱、订单号

上面第 1 条那道占位值拦截放过纯数字:不少公司的会员号本身就是自增整数,1 完全可能是真实的第一个会员。所以「不要用自增数字」这一条没有系统兜底,取值要在选型时定下来。

跨渠道认人 ​

在不同渠道传同一个 id,系统就会认成同一个人。客户先在你的网站上咨询过,之后从对话链接等别的渠道进来,只要传的是同一个值,他就能看到之前聊过的内容。

客服在工作台里看到的是:

  • 客户列表里一个人一行,不会因为换了渠道变成好几个客户
  • 客户档案里能看到他在所有渠道的对话记录,也能看到他还从哪些渠道来过
  • 手机号、邮箱、标签这些资料归拢到同一份档案里

认的是同一个人,对话仍按渠道各走各的。 同一个客户可能在网站和对话链接上各有一条进行中的对话,未读消息也是各渠道分别计算。客服接待时面对的仍是各自独立的对话。

没传 id 的匿名访客不参与这套认人,各渠道各自独立。

认人只看 id 这一个值。手机号、邮箱、姓名都只是资料,不参与判定:两次咨询填了同一个手机号,不会因此归成同一位客户。要让他们接上,就得传同一个 id。

两个方法 ​

拿到命令对象 hc 之后,身份相关的操作有两个。

方法什么时候调对已有资料的影响
hc.identify(...)客户登录后;资料变了再调一次传入的字段覆盖原值,没传的保留
hc.reset()客户退出登录清掉身份,结束当前对话

identify 的完整签名是 hc.identify({ id, profile, data });reset 之后历史对话和资料都保留,只是当前这通结束。

绑身份和写资料是同一个方法:第一次调把客户认出来,之后资料变了(改了昵称、升了会员等级)再调一次,传什么覆盖什么,没传的字段不动。你的系统就是客户资料的事实源。

换客户不需要你做额外处理:已经绑定了 A,直接 identify() 成 B,合从会先把上一位的痕迹清干净(换掉匿名标识、丢掉还没落库的资料、重新领取会话凭证),再写入新身份,两个人的对话不会串在一起。这个判定在页面刷新、重新打开之后依然有效。

js
hc.identify({
  id: 'c9a1f0e2-7b34-4d58-9f61-0ab2d3c4e5f6',
  profile: { name: '张三', phone: '13800138000', email: 'zhangsan@example.com' },
  data: { member_level: 'gold' }
})

三个方法都返回 Promise,成功后可以拿到写入结果。

网页接入渠道:客户没点开聊天窗时,这个 Promise 不会返回

访客进站不点咨询按钮,合从不会加载聊天窗、也不会连服务端。这期间调 identify(),命令会先存下来,等客户真正点开聊天窗再执行,Promise 也在那时才有结果。

所以别用 await hc.identify(...) 去挡自己页面的逻辑,那会一直等下去。要拿 acceptedKeys 调试,在聊天窗打开之后再看。对话链接是整页聊天,打开即执行,没有这个问题。

id 是字符串,1 到 255 字符,空值会被拒绝,不会把人串到一起。

profile:合从内置的资料字段 ​

字段类型上限超限会怎样
namestring100 字符截断后保存,不报错
phonestring20 字符截断后保存,不报错
emailstring255 字符截断后保存,不报错
avatarstring500 字符整次调用失败,且必须是 https:// 开头

头像和其他三个字段的失败方式不一样:地址不合规或超长,整个请求被拒,同一次传的姓名手机号一并丢失,客户仍然是匿名访客。其余三个超长只是截断,不影响别的字段。

手机号那条也提醒一下:带国际区号和分隔符的号码(如 +86 138-0000-0000)容易超过 20 字符,会被悄悄截成错号,客服拨过去打不通,而你收不到任何提示。传之前先规范化。

data:你自己的业务字段 ​

会员等级、积分、内部客户编号这类字段走 data。

js
hc.identify({ id: 'c9a1f0e2-7b34-4d58-9f61-0ab2d3c4e5f6', data: { member_level: 'gold', points: 3200, is_vip: true } })
约束说明
键名要先定义必须是工作台里建好的字段标识,见下方
键名格式小写字母开头,只能用小写字母、数字和下划线(member_level、store_no)
键名长度最长 64 字符,超了整次调用失败

键名不是随便起的:在工作台的 设置 > 客户字段 里新建字段,填的 字段标识 就是这里要用的键名,创建后不可修改。显示名(比如「会员等级」)只影响客服看到的标题,代码里认的始终是字段标识。 | 值的类型 | 字符串、数字、布尔值。对象和数组会让整次调用失败 | | 值的长度 | 字符串超过 200 字符会被截断后保存 |

没定义过的键会被丢弃,接口不报错,但浏览器控制台会列出被忽略的键名。一次传多个键时,没定义过的只丢它自己,其余照常写入。返回结果里的 acceptedKeys 是实际写进去的字段名,调试时对照它。

客服那边会看到资料变动 ​

资料真正写进客户名片之后,客服在这通对话里会看到一条灰字提示,右栏 对话动态 里能看到这次更新了哪些字段,来源标的是接入代码。

传的值和名片上现有的一样时不会提示,所以每次页面加载都调一次 identify() 不会在对话里刷屏。客户当时没有进行中的对话,资料照样写进名片,只是没有地方显示这条提示。

失败了怎么办 ​

调用失败时 Promise 会带回一个 code:

code含义你该怎么做
NETWORK请求没发出去或超时可以重试,此时客户仍是匿名访客
VAL_SCHEMA参数不合法,最常见的是头像地址违规检查参数,重试没有用
AUTH_TOKEN_INVALID会话凭证失效让页面刷新一次
AUTH_ORIGIN_DENIED当前域名不在渠道的授权域名里去 设置 > 渠道管理 > 安全与隐私 补上这个域名,重试没有用
SERVER服务端异常,或调用太频繁被限流隔一会儿再试。同一位访客 10 分钟内只能调 5 次,短时间反复调会一直失败

限流那条值得单独留意:它是按访客算的,页面每次加载都调一次属于正常用法,不会撞上;但如果你在轮询里调、或者每个字段一变就单独调一次,配额很快见底,真正需要的那次反而失败。资料变了攒到一起调一次,不要拆成好几次。

一定要接 .catch。 不接的话失败是完全静默的:客户变回匿名访客,页面上没有任何迹象,你也收不到告警。

共用设备的登出流程里一定要调 reset

换客户虽然不用手动 reset(),但那是下一位客户也登录的前提下 —— 他的 identify() 就是换人的触发点。

下一位客户不登录,就没有任何东西会触发换人:他顶着上一位的身份进来,看到上一位的历史对话。营业厅、展厅平板这类多人共用的机器最容易出这个问题。

所以客户退出登录时,在你自己的登出流程里调一次 hc.reset(),把身份收回来。

什么时候调 ​

window.hecong(cb) 早调晚调都能拿到命令对象,你要判断的只是业务上什么时候知道客户是谁。

js
// 直接在模板里输出,页面加载即绑定
window.hecong(hc => hc.identify({
  id: '<%= user.id %>',
  profile: { name: '<%= user.name %>' }
}))
js
async function onLoginSuccess(user) {
  window.hecong(hc => hc.identify({
    id: user.id,
    profile: { name: user.name }
  }))
}

多页站点每次跳转都会重新加载页面,所以每个页面都要调一次,不是登录时调一次就够。

一是命令和身份状态都存在页面内存里,跳转后就没了。二是资料要等访客点开聊天窗才真正发出,只在登录页调的话,访客在商品页点开聊天窗时是拿不到资料的。

客户登出时记得调 hc.reset(),原因见上面「共用设备的登出流程里一定要调 reset」。

React、Vue 项目的写法见 React 与 Vue 接入示例。

下一步 ​