Giao diện
Tham chiếu webhook
Mỗi sự kiện Tự Động Sale gửi tới webhook là một request POST JSON ký HMAC-SHA256; trang này liệt kê header, cách gửi lại, và từng loại sự kiện kèm bảng trường, ví dụ và JSON Schema.
Trang này sinh từ mã nguồn, không sửa tay.
Cách tạo webhook và xác minh chữ ký: Webhook. Sự kiện sắp ngừng hỗ trợ và quy tắc đổi hợp đồng: Thay đổi hợp đồng.
Mỗi lượt gửi mang những header nào?
Mỗi sự kiện là một request POST với Content-Type: application/json, kèm các header sau.
| Header | Ý nghĩa |
|---|---|
X-Tds-Event | Loại sự kiện, trùng trường type trong thân. |
X-Tds-Delivery | Mã lần gửi, trùng trường id trong thân; giữ nguyên giữa các lượt gửi lại. |
X-Tds-Timestamp | Thời điểm ký lượt gửi này, tính bằng giây Unix. Mỗi lượt gửi lại có giá trị mới. |
X-Tds-Signature | sha256= nối với chữ ký HMAC-SHA256 dạng hex của chuỗi <X-Tds-Timestamp>.<thân request nguyên văn>, khoá là secret của webhook. |
X-Tds-Test | Bằng 1 khi đây là lượt bấm "Gửi thử". Không có ở sự kiện thật. |
Cách tính lại chữ ký và mã mẫu: Webhook.
Hệ thống nhận phải trả lời thế nào?
Hệ thống nhận trả một mã HTTP 2xx trong 10 giây là xong. Mã khác, quá giờ hay không kết nối được thì Tự Động Sale gửi lại cùng sự kiện, tối đa 8 lượt trong khoảng 64 phút, khoảng chờ tăng dần từ 30 giây. Tự Động Sale không theo chuyển hướng: mã 3xx cũng tính là hỏng.
- Mọi lượt gửi lại giữ nguyên
id: lưuidđã xử lý và bỏ qua bản trùng. - Lượt gửi lại có thể đến sau một sự kiện mới hơn: với
lead.status_changed, chỉ áp sự kiện cóchanged_atmới hơn bản đã lưu. - Hết lượt gửi lại của 20 sự kiện liên tiếp thì webhook tự tắt và người dùng nhận thông báo trong ứng dụng; sửa hệ thống nhận rồi bật lại webhook.
Lượt "Gửi thử" khác sự kiện thật ở đâu?
Lượt bấm "Gửi thử" trong trang Kết nối Agent AI gửi đúng một lần, không gửi lại. Thân request có "test": true và id bắt đầu bằng test_, header có X-Tds-Test: 1, dữ liệu là mẫu cố định (mã bắt đầu bằng lead_test_). Gửi thử được cả sự kiện chưa đăng ký, để map trường trước khi bật nó. Luồng xử lý thật nên bỏ qua mọi payload có "test": true.
lead.matched — Có khách tiềm năng mới
Gửi một lần khi AI phân tích nội dung và tạo mới một khách tiềm năng. Người đã là khách tiềm năng mà xuất hiện lại ở bài khác thì không gửi lại.
Các trường trong thân request
| Trường | Kiểu | Luôn có | Mô tả |
|---|---|---|---|
id | string | Có | Mã lần gửi, không đổi giữa các lượt gửi lại của cùng một sự kiện: dùng để bỏ bản trùng. Lượt thử bắt đầu bằng test_. |
type | string | Có | Loại sự kiện. Luôn là "lead.matched". |
created_at | string | Có | Thời điểm Tự Động Sale tạo sự kiện. |
data | object | Có | Dữ liệu của sự kiện, tả ở các dòng bên dưới. |
data.lead_id | string | Có | Mã khách tiềm năng. |
data.topic_id | string | Có | Mã chủ đề tìm ra khách này. |
data.topic_name | string hoặc null | Có | Tên chủ đề, null nếu chưa đặt. |
data.author_name | string hoặc null | Có | Tên Facebook của người viết bài hoặc bình luận, null nếu không đọc được. |
data.profile_url | string hoặc null | Có | Link trang cá nhân Facebook của người đó, null nếu không có. |
data.source_url | string hoặc null | Có | Link bài viết hoặc bình luận nơi tìm ra khách, null nếu không có. |
data.score | number | Có | Điểm khớp AI trả về khi phân tích nội dung, từ 0 đến 1. |
data.lead_url | string | Có | Link mở thẳng khách tiềm năng này trong ứng dụng Tự Động Sale. |
test | boolean | Không | Chỉ có khi bấm "Gửi thử": dữ liệu mẫu, luồng thật nên bỏ qua. Luôn là true. |
deprecation | object | Không | Chỉ có khi sự kiện này sắp ngừng hỗ trợ: phần nào đổi và cách cập nhật. Chi tiết: Thay đổi hợp đồng. |
Ví dụ
json
{
"id": "dlv_3f9a1c0e7b2d4a6f8e1c3b5d7f9a0c2e",
"type": "lead.matched",
"created_at": "2026-10-03T08:30:01.000Z",
"data": {
"lead_id": "lead_test_0001",
"topic_id": "topic_test_0001",
"topic_name": "Tìm khách mua căn hộ",
"author_name": "Nguyễn Văn A",
"profile_url": "https://www.facebook.com/profile.php?id=100000000000001",
"source_url": "https://www.facebook.com/groups/100000000000002/posts/100000000000003",
"score": 0.86,
"lead_url": "https://app.tudongsale.com/crm?leadId=lead_test_0001"
}
}JSON Schema
json
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Mã lần gửi, không đổi giữa các lượt gửi lại của cùng một sự kiện: dùng để bỏ bản trùng. Lượt thử bắt đầu bằng `test_`."
},
"type": {
"type": "string",
"const": "lead.matched",
"description": "Loại sự kiện."
},
"created_at": {
"type": "string",
"format": "date-time",
"description": "Thời điểm Tự Động Sale tạo sự kiện."
},
"data": {
"type": "object",
"properties": {
"lead_id": {
"type": "string",
"description": "Mã khách tiềm năng."
},
"topic_id": {
"type": "string",
"description": "Mã chủ đề tìm ra khách này."
},
"topic_name": {
"type": [
"string",
"null"
],
"description": "Tên chủ đề, `null` nếu chưa đặt."
},
"author_name": {
"type": [
"string",
"null"
],
"description": "Tên Facebook của người viết bài hoặc bình luận, `null` nếu không đọc được."
},
"profile_url": {
"type": [
"string",
"null"
],
"description": "Link trang cá nhân Facebook của người đó, `null` nếu không có."
},
"source_url": {
"type": [
"string",
"null"
],
"description": "Link bài viết hoặc bình luận nơi tìm ra khách, `null` nếu không có."
},
"score": {
"type": "number",
"minimum": 0,
"maximum": 1,
"description": "Điểm khớp AI trả về khi phân tích nội dung, từ 0 đến 1."
},
"lead_url": {
"type": "string",
"format": "uri",
"description": "Link mở thẳng khách tiềm năng này trong ứng dụng Tự Động Sale."
}
},
"required": [
"lead_id",
"topic_id",
"topic_name",
"author_name",
"profile_url",
"source_url",
"score",
"lead_url"
],
"additionalProperties": true,
"description": "Dữ liệu của sự kiện, tả ở các dòng bên dưới."
},
"test": {
"type": "boolean",
"const": true,
"description": "Chỉ có khi bấm \"Gửi thử\": dữ liệu mẫu, luồng thật nên bỏ qua."
},
"deprecation": {
"type": "object",
"properties": {
"announcedOn": {
"type": "string",
"description": "Ngày thông báo ngừng hỗ trợ (YYYY-MM-DD)."
},
"removeAfter": {
"type": "string",
"description": "Sau ngày này (YYYY-MM-DD) tên cũ có thể ngừng hoạt động."
},
"replacedBy": {
"type": "string",
"description": "Tên thay thế, nếu có."
},
"changes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"type": "string",
"description": "Loại thay đổi. Giá trị hiện có: field_removed, field_renamed, type_changed, meaning_changed, input_now_required, scope_changed, tool_removed."
},
"field": {
"type": "string",
"description": "Trường bị đổi, nếu có."
},
"before": {
"type": "string",
"description": "Trước khi đổi."
},
"after": {
"type": "string",
"description": "Sau khi đổi."
},
"howToUpdate": {
"type": "string",
"description": "Cách cập nhật."
}
},
"required": [
"kind",
"before",
"after",
"howToUpdate"
],
"additionalProperties": true
},
"description": "Từng phần thay đổi và cách cập nhật."
},
"refreshSteps": {
"type": "array",
"items": {
"type": "string"
},
"description": "Cách làm mới danh sách lệnh ở từng ứng dụng Agent AI."
}
},
"required": [
"announcedOn",
"removeAfter",
"changes",
"refreshSteps"
],
"additionalProperties": true,
"description": "Chỉ có khi sự kiện này sắp ngừng hỗ trợ: phần nào đổi và cách cập nhật."
}
},
"required": [
"id",
"type",
"created_at",
"data"
],
"additionalProperties": true
}lead.status_changed — Khách đổi trạng thái
Gửi một lần mỗi khi trạng thái CRM của khách thật sự đổi, dù người dùng đổi tay, chuyển thành khách hàng hay luồng nhắn tin AI tự cập nhật. Lưu lại cùng trạng thái hoặc chỉ sửa số điện thoại, nhãn thì không gửi.
Các trường trong thân request
| Trường | Kiểu | Luôn có | Mô tả |
|---|---|---|---|
id | string | Có | Mã lần gửi, không đổi giữa các lượt gửi lại của cùng một sự kiện: dùng để bỏ bản trùng. Lượt thử bắt đầu bằng test_. |
type | string | Có | Loại sự kiện. Luôn là "lead.status_changed". |
created_at | string | Có | Thời điểm Tự Động Sale tạo sự kiện. |
data | object | Có | Dữ liệu của sự kiện, tả ở các dòng bên dưới. |
data.lead_id | string | Có | Mã khách tiềm năng. |
data.old_status | string | Có | Trạng thái CRM trước khi đổi. Giá trị hiện có: new, contacted, qualified, disqualified, closed, converted. |
data.new_status | string | Có | Trạng thái CRM sau khi đổi; converted khi khách được chuyển thành khách hàng. Giá trị hiện có: new, contacted, qualified, disqualified, closed, converted. |
data.changed_at | string | Có | Thời điểm trạng thái đổi. Lượt gửi lại có thể đến lệch thứ tự: chỉ áp sự kiện có changed_at mới hơn bản đã lưu. |
data.lead_url | string | Có | Link mở thẳng khách tiềm năng này trong ứng dụng Tự Động Sale. |
test | boolean | Không | Chỉ có khi bấm "Gửi thử": dữ liệu mẫu, luồng thật nên bỏ qua. Luôn là true. |
deprecation | object | Không | Chỉ có khi sự kiện này sắp ngừng hỗ trợ: phần nào đổi và cách cập nhật. Chi tiết: Thay đổi hợp đồng. |
Ví dụ
json
{
"id": "dlv_3f9a1c0e7b2d4a6f8e1c3b5d7f9a0c2e",
"type": "lead.status_changed",
"created_at": "2026-10-03T08:30:01.000Z",
"data": {
"lead_id": "lead_test_0001",
"old_status": "new",
"new_status": "contacted",
"changed_at": "2026-10-03T08:30:00.000Z",
"lead_url": "https://app.tudongsale.com/crm?leadId=lead_test_0001"
}
}JSON Schema
json
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Mã lần gửi, không đổi giữa các lượt gửi lại của cùng một sự kiện: dùng để bỏ bản trùng. Lượt thử bắt đầu bằng `test_`."
},
"type": {
"type": "string",
"const": "lead.status_changed",
"description": "Loại sự kiện."
},
"created_at": {
"type": "string",
"format": "date-time",
"description": "Thời điểm Tự Động Sale tạo sự kiện."
},
"data": {
"type": "object",
"properties": {
"lead_id": {
"type": "string",
"description": "Mã khách tiềm năng."
},
"old_status": {
"type": "string",
"description": "Trạng thái CRM trước khi đổi. Giá trị hiện có: new, contacted, qualified, disqualified, closed, converted."
},
"new_status": {
"type": "string",
"description": "Trạng thái CRM sau khi đổi; `converted` khi khách được chuyển thành khách hàng. Giá trị hiện có: new, contacted, qualified, disqualified, closed, converted."
},
"changed_at": {
"type": "string",
"format": "date-time",
"description": "Thời điểm trạng thái đổi. Lượt gửi lại có thể đến lệch thứ tự: chỉ áp sự kiện có `changed_at` mới hơn bản đã lưu."
},
"lead_url": {
"type": "string",
"format": "uri",
"description": "Link mở thẳng khách tiềm năng này trong ứng dụng Tự Động Sale."
}
},
"required": [
"lead_id",
"old_status",
"new_status",
"changed_at",
"lead_url"
],
"additionalProperties": true,
"description": "Dữ liệu của sự kiện, tả ở các dòng bên dưới."
},
"test": {
"type": "boolean",
"const": true,
"description": "Chỉ có khi bấm \"Gửi thử\": dữ liệu mẫu, luồng thật nên bỏ qua."
},
"deprecation": {
"type": "object",
"properties": {
"announcedOn": {
"type": "string",
"description": "Ngày thông báo ngừng hỗ trợ (YYYY-MM-DD)."
},
"removeAfter": {
"type": "string",
"description": "Sau ngày này (YYYY-MM-DD) tên cũ có thể ngừng hoạt động."
},
"replacedBy": {
"type": "string",
"description": "Tên thay thế, nếu có."
},
"changes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"type": "string",
"description": "Loại thay đổi. Giá trị hiện có: field_removed, field_renamed, type_changed, meaning_changed, input_now_required, scope_changed, tool_removed."
},
"field": {
"type": "string",
"description": "Trường bị đổi, nếu có."
},
"before": {
"type": "string",
"description": "Trước khi đổi."
},
"after": {
"type": "string",
"description": "Sau khi đổi."
},
"howToUpdate": {
"type": "string",
"description": "Cách cập nhật."
}
},
"required": [
"kind",
"before",
"after",
"howToUpdate"
],
"additionalProperties": true
},
"description": "Từng phần thay đổi và cách cập nhật."
},
"refreshSteps": {
"type": "array",
"items": {
"type": "string"
},
"description": "Cách làm mới danh sách lệnh ở từng ứng dụng Agent AI."
}
},
"required": [
"announcedOn",
"removeAfter",
"changes",
"refreshSteps"
],
"additionalProperties": true,
"description": "Chỉ có khi sự kiện này sắp ngừng hỗ trợ: phần nào đổi và cách cập nhật."
}
},
"required": [
"id",
"type",
"created_at",
"data"
],
"additionalProperties": true
}ping — Gửi thử
Chỉ gửi khi người dùng bấm "Gửi thử" trong trang Kết nối Agent AI, để kiểm tra hệ thống nhận và chữ ký.
Các trường trong thân request
| Trường | Kiểu | Luôn có | Mô tả |
|---|---|---|---|
id | string | Có | Mã lần gửi, không đổi giữa các lượt gửi lại của cùng một sự kiện: dùng để bỏ bản trùng. Lượt thử bắt đầu bằng test_. |
type | string | Có | Loại sự kiện. Luôn là "ping". |
created_at | string | Có | Thời điểm Tự Động Sale tạo sự kiện. |
data | object | Có | Dữ liệu của sự kiện, tả ở các dòng bên dưới. |
data.message | string | Có | Câu xác nhận webhook nhận được sự kiện từ Tự Động Sale. |
test | boolean | Không | Chỉ có khi bấm "Gửi thử": dữ liệu mẫu, luồng thật nên bỏ qua. Luôn là true. |
deprecation | object | Không | Chỉ có khi sự kiện này sắp ngừng hỗ trợ: phần nào đổi và cách cập nhật. Chi tiết: Thay đổi hợp đồng. |
Ví dụ
json
{
"id": "test_5f0c2a8e-1b7d-4c3e-9a61-2d4b8f0e7c19",
"type": "ping",
"created_at": "2026-10-03T08:30:01.000Z",
"data": {
"message": "Webhook của bạn đã nhận được sự kiện từ Tự Động Sale."
},
"test": true
}JSON Schema
json
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Mã lần gửi, không đổi giữa các lượt gửi lại của cùng một sự kiện: dùng để bỏ bản trùng. Lượt thử bắt đầu bằng `test_`."
},
"type": {
"type": "string",
"const": "ping",
"description": "Loại sự kiện."
},
"created_at": {
"type": "string",
"format": "date-time",
"description": "Thời điểm Tự Động Sale tạo sự kiện."
},
"data": {
"type": "object",
"properties": {
"message": {
"type": "string",
"description": "Câu xác nhận webhook nhận được sự kiện từ Tự Động Sale."
}
},
"required": [
"message"
],
"additionalProperties": true,
"description": "Dữ liệu của sự kiện, tả ở các dòng bên dưới."
},
"test": {
"type": "boolean",
"const": true,
"description": "Chỉ có khi bấm \"Gửi thử\": dữ liệu mẫu, luồng thật nên bỏ qua."
},
"deprecation": {
"type": "object",
"properties": {
"announcedOn": {
"type": "string",
"description": "Ngày thông báo ngừng hỗ trợ (YYYY-MM-DD)."
},
"removeAfter": {
"type": "string",
"description": "Sau ngày này (YYYY-MM-DD) tên cũ có thể ngừng hoạt động."
},
"replacedBy": {
"type": "string",
"description": "Tên thay thế, nếu có."
},
"changes": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"type": "string",
"description": "Loại thay đổi. Giá trị hiện có: field_removed, field_renamed, type_changed, meaning_changed, input_now_required, scope_changed, tool_removed."
},
"field": {
"type": "string",
"description": "Trường bị đổi, nếu có."
},
"before": {
"type": "string",
"description": "Trước khi đổi."
},
"after": {
"type": "string",
"description": "Sau khi đổi."
},
"howToUpdate": {
"type": "string",
"description": "Cách cập nhật."
}
},
"required": [
"kind",
"before",
"after",
"howToUpdate"
],
"additionalProperties": true
},
"description": "Từng phần thay đổi và cách cập nhật."
},
"refreshSteps": {
"type": "array",
"items": {
"type": "string"
},
"description": "Cách làm mới danh sách lệnh ở từng ứng dụng Agent AI."
}
},
"required": [
"announcedOn",
"removeAfter",
"changes",
"refreshSteps"
],
"additionalProperties": true,
"description": "Chỉ có khi sự kiện này sắp ngừng hỗ trợ: phần nào đổi và cách cập nhật."
}
},
"required": [
"id",
"type",
"created_at",
"data"
],
"additionalProperties": true
}