# Webhook khách tiềm năng mới và đổi trạng thái

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

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

> Webhook gửi sự kiện từ Tự Động Sale tới hệ thống của bạn (n8n, Zapier, CRM) mỗi khi có khách tiềm năng mới hoặc trạng thái CRM của khách đổi. Mỗi request được ký bằng secret riêng của webhook để hệ thống nhận xác minh được nguồn gửi.

## Webhook gửi những sự kiện nào?

Webhook gửi ba loại sự kiện:

- `lead.matched` — có khách tiềm năng mới;
- `lead.status_changed` — trạng thái CRM của khách thật sự đổi, dù bạn đổi tay, chuyển khách thành khách hàng hay luồng nhắn tin AI tự cập nhật;
- `ping` — chỉ gửi khi bạn bấm **Gửi thử**.

Bảng trường, header, ví dụ và JSON Schema của từng sự kiện nằm ở trang [Tham chiếu webhook](/agent-ai/tham-chieu-webhook). Payload không có nội dung tin nhắn Messenger.

## Tạo webhook thế nào?

1. Mở trang [Kết nối Agent AI](https://app.tudongsale.com/settings/ket-noi-agent) (menu Tài khoản & Thanh toán), tới khối **Webhook** và bấm **Thêm webhook**.
2. Dán URL nhận sự kiện của hệ thống bạn. URL phải bắt đầu bằng `https://`, dùng cổng mặc định và trỏ tới một địa chỉ công khai trên internet.
3. Chọn sự kiện muốn nhận; mặc định chọn cả `lead.matched` lẫn `lead.status_changed`.
4. Lưu lại. Secret của webhook (bắt đầu bằng `whsec_`) chỉ hiện **một lần** ngay sau khi tạo: chép vào hệ thống nhận trước khi đóng hộp thoại.
5. Bấm **Gửi thử** để nhận một sự kiện mẫu và kiểm tra chữ ký.

Mỗi tài khoản có tối đa 3 webhook. Webhook chỉ tạo được trong ứng dụng: Agent AI kết nối với tài khoản không có lệnh nào tạo hay sửa webhook.

## Xác minh chữ ký thế nào?

Mỗi request có header `X-Tds-Timestamp` (giây Unix) và `X-Tds-Signature` dạng `sha256=<hex>`. Chữ ký là HMAC-SHA256 của chuỗi `<X-Tds-Timestamp>.<thân request nguyên văn>`, khoá là nguyên chuỗi secret (kể cả tiền tố `whsec_`). Tính trên thân request nguyên văn, trước khi parse JSON: parse rồi stringify lại có thể đổi thứ tự hay khoảng trắng và làm sai chữ ký.

Mã mẫu Node.js:

```js
import { createHmac, timingSafeEqual } from 'node:crypto'

/** rawBody: thân request nguyên văn (chuỗi), headers: header đã viết thường. */
export function verifyTudongsaleWebhook(rawBody, headers, secret) {
  const timestamp = headers['x-tds-timestamp'] ?? ''
  const received = Buffer.from(headers['x-tds-signature'] ?? '')
  const expected = Buffer.from(
    'sha256=' +
      createHmac('sha256', secret)
        .update(`${timestamp}.${rawBody}`)
        .digest('hex'),
  )
  const signatureOk =
    received.length === expected.length && timingSafeEqual(received, expected)
  // Chặn request cũ bị phát lại: chỉ nhận chữ ký ký trong 5 phút gần đây.
  const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300
  return signatureOk && fresh
}
```

Chữ ký sai hoặc quá cũ thì trả mã 401 và bỏ qua request.

## Xử lý gửi trùng và lệch thứ tự thế nào?

Tự Động Sale gửi lại một sự kiện khi hệ thống nhận không trả mã 2xx kịp, nên cùng một sự kiện có thể tới hơn một lần và tới muộn hơn sự kiện mới hơn:

- **Bỏ bản trùng theo `id`.** Mọi lượt gửi lại của cùng một sự kiện giữ nguyên `id` (cũng là header `X-Tds-Delivery`). Lưu `id` đã xử lý, gặp lại thì trả 2xx và bỏ qua.
- **Áp `lead.status_changed` theo `changed_at`.** Chỉ cập nhật trạng thái khi `changed_at` của sự kiện mới hơn bản đang lưu cho khách đó; sự kiện cũ tới muộn thì bỏ qua.
- **Đừng so `changed_at` với `created_at`.** `changed_at` là lúc trạng thái đổi, ghi theo đồng hồ cơ sở dữ liệu; `created_at` là lúc tạo sự kiện, ghi theo đồng hồ máy chủ gửi. Hai đồng hồ có thể lệch nhau vài phần giây, nên chỉ so `changed_at` với `changed_at`.
- **`old_status` có thể đã cũ** khi hai thay đổi trạng thái của cùng một khách xảy ra gần như cùng lúc: hai sự kiện có thể mang cùng `old_status`. Coi `new_status` kèm `changed_at` là sự thật, `old_status` chỉ để tham khảo.

## Khi nào webhook ngừng gửi?

Webhook ngừng gửi khi:

- bạn tắt hoặc xoá webhook — lượt gửi lại đang chờ cũng dừng;
- bạn đổi URL — lượt gửi lại của sự kiện cũ không đi tới URL mới;
- bạn bỏ chọn một sự kiện — lượt gửi lại đang chờ của sự kiện đó dừng;
- webhook tự tắt sau nhiều sự kiện liên tiếp gửi không được (số lượt và ngưỡng ở trang [Tham chiếu webhook](/agent-ai/tham-chieu-webhook)). Bạn nhận thông báo trong ứng dụng; sửa hệ thống nhận rồi bấm **Bật lại**.

Đổi mật khẩu hay đăng xuất mọi nơi không tắt webhook. Lượt **Gửi thử** hỏng không tính vào việc tự tắt.

## Xoay secret thế nào?

Chọn **Xoay secret** trên webhook trong trang Kết nối Agent AI. Secret mới chỉ hiện một lần; từ lượt gửi kế tiếp (kể cả lượt gửi lại của sự kiện cũ), chữ ký dùng secret mới, nên hệ thống nhận cần cập nhật secret ngay.

## Tự Động Sale đổi sự kiện webhook theo quy tắc nào?

Tự Động Sale chỉ thêm trường mới vào sự kiện đang chạy; thay đổi phá vỡ luôn ra một sự kiện mới và sự kiện cũ vẫn được gửi một thời gian. Quy tắc, lịch ngừng và cách chuyển luồng sang sự kiện mới nằm ở trang [Thay đổi hợp đồng](/agent-ai/thay-doi-hop-dong). Trong thời gian chờ ngừng, payload của sự kiện cũ có trường `deprecation` nói rõ phần nào đổi.
