C cenaly.com
免费开始

公共 API 与 Webhook

权限密钥、线索/属性/事件/对话和客群的 REST API、新线索及事件 Webhook —— 附带 curl 示例

3 min read 管理后台演示
On this page 9

公开 API 与 Webhook

REST API 让您的 CRM、网站后端或自有机器人能够像面板一样操作 cenaly.com 的线索和对话:读取卡片、修改属性、发送事件、在对话中回复。Webhook 则相反,由它主动通知您的服务器有新的线索、事件和消息。

启动所需条件#

项目 位置
角色 账户所有者——密钥以其名义生效;员工看不到密钥
板块 已启用的聊天(线索和对话都在其中)
密钥 集成 → API 与 Webhook → 密钥 → 创建密钥
API 地址 https://api.cenaly.com/public-api/v1
方法参考 api.cenaly.com/public-api/v1/docs
OpenAPI 3.1 api.cenaly.com/public-api/v1/openapi.json

第 1 步:密钥与权限#

  1. 打开集成,点击页面顶部的 API 与 Webhook。
  2. 在密钥选项卡中点击创建密钥,输入名称(“CRM”、“网站后端”、“Telegram 机器人”)。
  3. 选择权限集或逐项勾选权限,若有多个门店——选择密钥可访问的门店,以及有效期。
  4. 复制密钥 cnk_…。它只显示一次:我们仅保存其指纹(SHA-256)。
权限 作用
contacts:read 线索列表、搜索和卡片、属性
contacts:write 修改属性和标签、合并、按数据主体请求删除
events:read 线索事件流
events:write 发送事件(购买、注册、任意自定义事件)
conversations:read 对话和消息
conversations:write 客服回复、备注、状态、负责人、标记
messages:send 向线索的最近对话发送消息
segments:read 已保存的细分及其成员

权限集:CRM / 后端(线索、属性、事件、读取对话和细分)、自有机器人(读取线索、在对话中回复、向线索写入)、只读、全部。

  • 权限可即时修改密钥的权限和门店——集成继续正常工作。
  • 重新签发会生成新密钥;旧密钥立即失效,权限和日志保留。
  • 撤销——立即且永久生效,下一次请求将收到 401。

密钥通过 Authorization: Bearer cnk_… 请求头传递(或 X-Api-Key: cnk_…)。请仅在服务器上保存——不要放在浏览器或移动应用中。

第 2 步:首次请求#

KEY=cnk_ваш_ключ
API=https://api.cenaly.com/public-api/v1

# 密钥能做什么:权限、门店、限额
curl -H "Authorization: Bearer $KEY" "$API/me"

# 线索,最新的在前(每页 20 条)
curl -H "Authorization: Bearer $KEY" "$API/contacts?limit=20"

# 按姓名、邮箱或电话搜索
curl -H "Authorization: Bearer $KEY" "$API/contacts?q=anna"

响应始终是信封格式:

{
  "meta": { "status": 200, "requestId": "…", "nextCursor": "WyIyMDI2LTEwLTA0…" },
  "data": { "total": 128, "contacts": [ { "id": "…", "status": "lead", "name": "Анна", "updatedAt": "2026-10-04T09:12:00Z" } ] }
}

下一页——同一请求加上 cursor=<meta.nextCursor>;没有 nextCursor——就没有更多页了。错误——{"meta": {...}, "error": {"code": "…"}}。

门店。 如果密钥只能访问一个门店,无需指定。如果有多个——请添加 location=<门店 id>(列表——GET /v1/locations),否则响应为 400 location_required。

按您的 user_id 操作属性和事件。 不必保存我们的线索 id:添加 ?by=user_id,地址就变成您的用户标识符。若该 user_id 的线索尚不存在——我们会创建它。

# 属性:操作 update_or_create、set_once、add、delete、append、union、exclude
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"ops":[{"op":"update_or_create","key":"$email","value":"anna@example.com"},
              {"op":"add","key":"orders","value":1},
              {"op":"union","key":"interests","value":"yoga"}]}' \
  "$API/contacts/u-1001/props?by=user_id"

# 批量事件(最多 100 条),相同 id 重复提交只计一次
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"events":[{"id":"ord-5531","userId":"u-1001","name":"order_paid","props":{"sum":45,"currency":"GEL"}}]}' \
  "$API/events"

# 在对话中回复并更改状态
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"text":"Здравствуйте! Заказ передан курьеру."}' "$API/conversations/<id>/reply"
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"status":"resolved"}' "$API/conversations/<id>/status"

写入经过与面板编辑相同的队列:变更在数秒内生效,按 user_id 写入的响应为 202 并带 pending。在线索卡片中,修改者显示为“API: <密钥名称>”。

方法#

方法 作用 权限
GET /v1/me, GET /v1/locations 密钥、权限、门店 —
GET /v1/contacts 线索:status(anon、lead、user)、q、limit、cursor contacts:read
GET /v1/contacts/{id} 卡片:属性、标签、事件摘要 contacts:read
POST /v1/contacts/{id}/props 以操作方式更新属性(最多 250 个)或 {"props":{…}} contacts:write
POST / DELETE /v1/contacts/{id}/tags 线索标签 contacts:write
POST /v1/contacts/merge 合并两张卡片:先预览,然后 "apply": true contacts:write
POST /v1/contacts/{id}/erase 应数据主体请求删除("confirm": true) contacts:write
GET /v1/contacts/{id}/events 线索事件流 events:read
POST /v1/contacts/{id}/events, POST /v1/events 事件(每次最多 100 个) events:write
GET /v1/contacts/{id}/conversations 线索的对话 conversations:read
POST /v1/contacts/{id}/messages 在最近一次对话中给线索发消息 messages:send
GET /v1/conversations, /{id}, /{id}/messages 对话和消息 conversations:read
POST /v1/conversations/{id}/reply 客服回复或备注("type": "note") conversations:write
POST /v1/conversations/{id}/status, /assign, /tags 状态、负责人、标记 conversations:write
GET /v1/segments, GET /v1/segments/{id}/contacts 分群及其成员 segments:read

每个方法的字段、示例和错误码见参考文档。

第 3 步. 网络钩子#

网络钩子在门店中配置:聊天 → 设置 → 网络钩子 — https 地址、事件和签名密钥。除对话事件(新对话、新消息、状态、评价、标记)外,新增了线索事件:

事件 触发时机
lead.created 出现新线索(联系人在聊天、表单中留下,或通过 API 创建)
lead.props_changed 线索属性发生变化 — 变更的键列表在 changedProps 中
lead.event_tracked 线索发生事件;默认发送您的全部事件,不含系统事件($…),名称列表由“发送哪些事件”字段指定
message.goal_reached 线索达成消息目标(弹窗、群发)
{
  "event": "lead.event_tracked",
  "deliveryId": "WD…",
  "at": "2026-10-04T09:12:03Z",
  "webhookId": "…",
  "locationId": "…",
  "contact": { "id": "…", "status": "user", "userId": "u-1001", "props": { "orders": 3 }, "tags": ["vip"] },
  "leadEvent": { "name": "order_paid", "at": "2026-10-04T09:12:00Z", "props": { "sum": 45 } }
}

只有当网络钩子勾选“传递访客联系方式”时,线索的姓名、邮箱和电话才会出现在请求体中。签名校验 — 用网络钩子密钥对请求体做 HMAC-SHA256:

# заголовок X-Meni-Signature: sha256=<hex>
echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET"

在 5 秒内返回 2xx — 否则会重试投递;连续多次失败后网络钩子会被停用,这在设置中可见。重复投递请按 deliveryId 去重。

限制#

  • 每个密钥每分钟 60 个请求;超出则返回 429 并带 Retry-After 响应头。剩余额度在 X-RateLimit-* 中。
  • 每页最多 200 条记录(默认 50),每批最多 100 个事件,每个请求最多 250 个属性操作,每次最多 30 个标签。
  • 每个账户最多 20 个有效密钥。
  • 密钥调用日志保留 30 天:时间、方法、路径模板(不含 id 和请求体)、响应码、耗时。
  • 通过 API 发送的事件会进入线索卡片(事件流、摘要、分群),但不会进入原始访问分析日志。
  • 只能向已存在的对话给线索发消息:没有对话则返回 409 no_conversation。

故障排查#

响应 含义 处理方式
401 api_key_missing / api_key_invalid 请求中没有密钥,或密钥错误、已撤销、已过期 检查请求头;必要时重新签发密钥
403 scope_required 密钥缺少所需权限 在密钥的权限中添加权限
400 location_required 密钥可访问多个门店 添加 location=<id>
404 location_not_found 该门店不在密钥的门店中 对照 GET /v1/locations 核对
404 not_found 线索或对话不存在 对于您自己的 id,请使用 ?by=user_id
429 rate_limited 超过每分钟 60 个请求 等待 Retry-After 秒,将事件分批发送
502 upstream_error 临时故障 稍后重试请求

每个请求都可在调用日志标签页中查看 — 含响应码和密钥名称。

常见问题#

可以从浏览器调用 API 吗? 不可以:密钥可访问门店的全部线索。请从自己的服务器调用 API。

user_id 与线索 id 有什么区别? 线索 id 是卡片在我们系统中的标识。user_id 是您系统中的用户标识(CRM 中的编号、网站上的账户 id);用 ?by=user_id 可以通过它定位卡片。

重复发送事件会怎样? 具有相同 id 的事件只计一次 — 网络出错时可以放心重试请求。

每个门店需要单独的密钥吗? 不需要:一个密钥可以操作多个门店,门店通过 location 参数选择。

相关章节#

Was this article helpful?