从您的网站获取线索
将您的网站表单与 ReactiveCRM 集成的说明:将表单提交捕获为线索、发送营销归因事件并验证结果。
1. 概述
推荐流程由两个请求组成:
- 网站将表单数据发送到自己的后端。
- 网站后端通过
POST /api/leads/create在 CRM 中创建线索。 - 创建成功后,后端通过
POST /api/dashboard/marketing/events发送lead_submitted营销事件,并传递收到的leadId。 - CRM 存储 UTM 标签、广告标识符和引荐来源以用于营销归因。
网站表单
|
v
网站后端 / 服务端代理
| POST /api/leads/create
v
ReactiveCRM -> leadId
| POST /api/dashboard/marketing/events
v
营销归因与仪表板
不要从浏览器直接发送 CRM JWT 和机密信息。使用网站后端或 Serverless 函数,以确保 CRM 令牌和服务标识符永远不会暴露给访客。
2. 认证与基础 URL
所有请求均在相关租户的上下文中运行,并需要 CRM 授权:
Authorization: Bearer <CRM_ACCESS_TOKEN>
Content-Type: application/json
示例中使用:
CRM_BASE_URL=https://crm.example.com
请将其替换为您环境的 URL。
3. 在网站上收集的数据
线索数据
要创建线索,API 支持:
| 字段 | 必填 | 描述 |
|---|---|---|
message | 否 | 表单中的咨询消息或文本 |
ownerId | 是 | 负责该线索的 CRM 用户 UUID |
authorId | 是 | 创建该线索的 CRM 用户 UUID |
clientId | 否 | 现有客户 UUID |
contactId | 否 | 现有联系人 UUID |
leadSourceId | 否 | 线索来源树中的节点 UUID |
statusId | 否 | 初始状态 UUID |
quality | 否 | HOT、WARM、COOL 或 COLD |
ownerId 和 authorId 是 CRM 用户的 UUID。对于公开的网站表单,不要让访客自行设置这些值:后端必须从集成配置中提供它们。
营销访问数据
在表单提交之前,将以下内容存储在 cookie、会话存储或后端中:
utm_source→utmSourceutm_medium→utmMediumutm_campaign→utmCampaignutm_content→utmContentutm_term→utmTermgclid— Google Ads 标识符fbclid— Meta/Facebook 标识符- 着陆页 URL →
landingUrl - 上一页 URL →
referrer - 您自己的访客和会话标识符 →
visitorId、sessionId
在首次访问时保存这些值,并在网站内导航时不要用空参数覆盖它们。
4. 创建线索
端点
POST /api/leads/create
请求示例
curl -X POST "$CRM_BASE_URL/api/leads/create" \
-H "Authorization: Bearer $CRM_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"message": "网站咨询:咨询请求",
"ownerId": "11111111-1111-1111-1111-111111111111",
"authorId": "11111111-1111-1111-1111-111111111111",
"quality": "WARM"
}'
201 Created 响应示例
{
"id": "22222222-2222-2222-2222-222222222222",
"message": "网站咨询:咨询请求",
"quality": "WARM",
"dealId": null,
"createdAt": "2026-09-05T20:00:00Z",
"updatedAt": "2026-09-05T20:00:00Z",
"version": 0,
"author": {
"id": "11111111-1111-1111-1111-111111111111",
"firstName": "CRM",
"lastName": "机器人"
},
"owner": {
"id": "11111111-1111-1111-1111-111111111111",
"firstName": "CRM",
"lastName": "机器人"
}
}
保存响应中的 id:该值将传递到营销事件的 leadId 字段中。
5. 发送线索提交事件
端点
POST /api/dashboard/marketing/events
请求示例
curl -X POST "$CRM_BASE_URL/api/dashboard/marketing/events" \
-H "Authorization: Bearer $CRM_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"eventType": "lead_submitted",
"occurredAt": "2026-09-05T20:00:00Z",
"visitorId": "visitor-8b7f",
"sessionId": "session-31ac",
"leadId": "22222222-2222-2222-2222-222222222222",
"externalId": "site-form-submit-20260905-000123",
"source": "website",
"channel": "paid",
"utmSource": "google",
"utmMedium": "cpc",
"utmCampaign": "summer-consulting",
"utmContent": "banner-a",
"utmTerm": "crm consultation",
"gclid": "EAIaIQobChMI-example",
"landingUrl": "https://www.example.com/consultation",
"referrer": "https://www.google.com/",
"metadata": {
"formName": "consultation",
"pageType": "landing"
}
}'
响应示例
{
"accepted": true,
"duplicate": false,
"eventId": "33333333-3333-3333-3333-333333333333"
}
如果同一个 externalId 已经针对此 source 和租户对处理过,API 会返回一个带有 duplicate: true 的成功响应。请将其视为成功而非错误,并且不要创建第二个线索。
6. 服务端处理程序示例
下面是一个简化的 Node.js 示例。表单数据必须发送到网站后端,而不是从浏览器直接发送到 CRM。
async function createWebsiteLead(form, attribution) {
const headers = {
Authorization: `Bearer ${process.env.CRM_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
};
const leadResponse = await fetch(
`${process.env.CRM_BASE_URL}/api/leads/create`,
{
method: 'POST',
headers,
body: JSON.stringify({
message: form.message || `网站咨询:${form.subject || '无主题'}`,
ownerId: process.env.CRM_LEAD_OWNER_ID,
authorId: process.env.CRM_LEAD_AUTHOR_ID,
quality: 'WARM',
}),
},
);
if (!leadResponse.ok) {
throw new Error(`CRM 线索创建失败:${leadResponse.status}`);
}
const lead = await leadResponse.json();
const externalId = `website-${form.requestId}`;
const eventResponse = await fetch(
`${process.env.CRM_BASE_URL}/api/dashboard/marketing/events`,
{
method: 'POST',
headers,
body: JSON.stringify({
eventType: 'lead_submitted',
occurredAt: new Date().toISOString(),
visitorId: attribution.visitorId,
sessionId: attribution.sessionId,
leadId: lead.id,
externalId,
source: 'website',
channel: attribution.channel || 'direct',
utmSource: attribution.utmSource,
utmMedium: attribution.utmMedium,
utmCampaign: attribution.utmCampaign,
utmContent: attribution.utmContent,
utmTerm: attribution.utmTerm,
gclid: attribution.gclid,
fbclid: attribution.fbclid,
landingUrl: attribution.landingUrl,
referrer: attribution.referrer,
metadata: { formName: form.formName || 'website' },
}),
},
);
if (!eventResponse.ok) {
// 线索已创建。将事件加入队列并使用相同的 externalId 重试,
// 而不是创建第二个线索。
throw new Error(`CRM 营销事件失败:${eventResponse.status}`);
}
return { leadId: lead.id, marketingEvent: await eventResponse.json() };
}
7. 幂等性与重试
- 为每个表单提交生成一个稳定的
externalId。例如,使用在首次请求之前创建的 UUID,或唯一的表单requestId。 - 发生网络错误时,使用相同的
externalId重试营销事件请求。 - 重试时不要生成新的
externalId:这会导致重复事件。 - 如果线索创建请求因超时而以未知结果结束,不要盲目创建新线索。首先使用您自己的
requestId和网站后端的去重机制,或在 CRM 中查找已创建的线索。 - 事件发送错误不应导致用户在未检查线索创建结果的情况下重新提交表单。
8. 个人数据与元数据
禁止在 metadata 中传递个人数据和表单内容:
- 邮箱
- 电话
- 姓名
- 地址
- 消息文本
- 包含个人数据的标识符的 Cookie
个人数据只能在其对应的 CRM 实体中传递 — 例如,在预先创建的 contactId/clientId 中。在 metadata 中只保留技术属性:表单名称、页面类型、横幅变体等。
9. 典型响应与错误
| HTTP | 情况 | 处理方法 |
|---|---|---|
201 | 线索已创建 | 保存 id 并发送 lead_submitted |
200 + duplicate: false | 事件已接受 | 将 eventId 保存到集成日志中 |
200 + duplicate: true | 事件已被接受 | 视为已处理;不要再次发送 |
400 | 数据无效或 metadata 中包含 PII | 修复负载;不修改重试无效 |
401 | 令牌无效或缺失 | 在后端刷新 CRM 令牌 |
403 | 令牌缺乏操作权限 | 检查角色和租户 |
5xx / 超时 | 临时 CRM 或网络错误 | 使用相同的 externalId 重试;对于线索,使用队列 |
10. 在 CRM 中验证结果
集成后,验证:
- 线索通过
GET /api/leads/{id}或在GET /api/leads/paged列表中出现。 - 线索的所有者、作者和创建时间正确。
- 首次发送时营销事件响应的
duplicate为false。 - 重新发送相同的提交返回
duplicate: true。 - 在营销仪表板中,数据显示在正确的时段、渠道和活动中。
获取已创建的线索
curl "$CRM_BASE_URL/api/leads/$LEAD_ID" \
-H "Authorization: Bearer $CRM_ACCESS_TOKEN"
获取用于列表或对账的线索
curl "$CRM_BASE_URL/api/leads/paged?page=0&size=20&createdAtFrom=2026-09-05T00:00:00Z&createdAtTo=2026-09-05T23:59:59Z&sort=createdAt,desc" \
-H "Authorization: Bearer $CRM_ACCESS_TOKEN"
page 参数从 0 开始;默认 size 为 20。要获取网站线索,如果相关数据已存储在 CRM 中,请额外使用 message、createdAtFrom/createdAtTo、contactEmail 或 search 过滤器。
Reactive CRM