# Tham chiếu webhook

Source: https://wiki.tudongsale.com/agent-ai/tham-chieu-webhook

Cập nhật: 2026-10-04

> 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](/agent-ai/webhook). Sự kiện sắp ngừng hỗ trợ và quy tắc đổi hợp đồng: [Thay đổi hợp đồng](/agent-ai/thay-doi-hop-dong).

## 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](/agent-ai/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ưu `id` đã 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_at` mớ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 {#lead-matched data-h2-exempt}

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](/agent-ai/thay-doi-hop-dong). |

**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 {#lead-status-changed data-h2-exempt}

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](/agent-ai/thay-doi-hop-dong). |

**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ử {#ping data-h2-exempt}

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](/agent-ai/thay-doi-hop-dong). |

**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
}
```
