JavaScript API
Widget cung cấp một API nhỏ qua window.$hatang. Dùng nó để mở chat từ nút của bạn, cho hộp thư biết khách là ai, thêm ngữ cảnh cho nhân viên, gửi lead từ form của bạn và phản ứng khi có tin nhắn mới.
Trước khi bắt đầu
Cài widget bằng đoạn mã hai thẻ. Thẻ thứ nhất tạo window.$hatang dưới dạng hàng đợi, nên bạn gọi lệnh được cả trước khi widget tải xong:
<script>window.$hatang=window.$hatang||[];window.HATANG_SITE_ID="site_123...";</script>
<script src="https://chat.hatang.dev/widget.js" async></script>
Cách lệnh hoạt động
Mỗi lệnh là một mảng: tên lệnh, rồi tới các tham số.
window.$hatang = window.$hatang || [];
$hatang.push(["open"]);
- Trước khi widget tải xong, lệnh nằm trong hàng đợi và chạy theo thứ tự khi widget khởi động.
- Sau khi tải xong,
$hatang.pushchạy lệnh ngay. Các lệnh cũng có dạng hàm, ví dụ$hatang.open(). - Lệnh không tồn tại bị bỏ qua. Lệnh không bao giờ throw, nên một lỗi nhỏ không làm hỏng trang.
Lệnh
| Lệnh | Tham số | Tác dụng |
|---|---|---|
open | không | Mở khung chat. |
close | không | Đóng khung chat. |
toggle | không | Mở khung nếu đang đóng, đóng nếu đang mở. |
hide | không | Ẩn nút chat. Khung đang mở vẫn mở. |
show | không | Hiện lại nút chat (kể cả sau hideLauncher). |
identify | { user_id?, email?, name?, phone?, hash?, data? } | Cho hộp thư biết khách là ai. Xem identify. |
setData | { ...thuộc tính } | Thêm thuộc tính tuỳ chỉnh cho nhân viên xem. Khoá mới gộp vào dữ liệu đã có. |
setLocale | "vi" hoặc "en" | Đổi ngôn ngữ widget và ghi nhớ trên trình duyệt này. |
pushEvent | name, { ...data } | Ghi một sự kiện, ví dụ "viewed_pricing": vào hội thoại đang mở, hoặc vào hành trình của khách trước khi nhắn. Xem pushEvent. |
lead | { name?, phone?, email?, message?, source?, fields? } | Tạo lead từ form của bạn, không cần mở chat. Xem Lead từ form của bạn. |
unreadCount | callback(count) | Gọi callback với số tin trả lời chưa đọc hiện tại. |
on | event, callback | Đăng ký nghe một sự kiện. Xem Sự kiện. |
off | event, callback | Huỷ callback đã đăng ký bằng on. Truyền đúng hàm đó. |
reset | không | Quên khách trên trình duyệt này và tải lại trang. Gọi khi người dùng đăng xuất. |
Mở chat từ phần tử bất kỳ
Thêm data-hatang-open vào phần tử bất kỳ. Bấm vào phần tử đó, hoặc vào thứ nằm trong nó, sẽ mở chat. Không cần JavaScript:
<button data-hatang-open>Chat với chúng tôi</button>
<a href="#" data-hatang-open>Cần tư vấn? Nhắn ngay</a>
Muốn chỉ dùng nút của mình, ẩn nút chat bằng window.HATANG_CONFIG = { hideLauncher: true } và tự hiện số tin chưa đọc qua sự kiện unread.
identify
$hatang.push(["identify", {
user_id: "u_42",
name: "Nguyễn Thu Hà",
phone: "0912345678",
hash: "<HMAC từ server của bạn>",
data: { plan: "pro", orders: 12 }
}]);
| Trường | Kiểu | Giới hạn |
|---|---|---|
user_id | string | Tối đa 200 ký tự. ID người dùng trong hệ thống của bạn. |
email | string | Email hợp lệ, tối đa 320 ký tự. |
name | string | Tối đa 200 ký tự. |
phone | string | Tối đa 40 ký tự. |
hash | string | HMAC-SHA256(identity secret, user_id) dạng hex, tính ở server. |
data | object | Thuộc tính tuỳ chỉnh, tối đa 8 KB dạng JSON. |
Không có hash, thông tin được lưu là thông tin khách tự khai và hộp thư gắn nhãn Chưa xác minh. Có hash hợp lệ, hộp thư gắn nhãn Đã xác minh và nối mọi phiên của người dùng đó. Đọc Nhận diện khách trước khi dùng user_id.
Tên trường là user_id, viết kiểu snake case. Các khoá khác bị bỏ đi. Nếu email không hợp lệ hoặc một giá trị vượt giới hạn, cả lệnh identify bị từ chối, nên hãy kiểm tra dữ liệu trước.
setData
$hatang.push(["setData", { plan: "pro", cart_value: 450000, last_order: "DH-1042" }]);
setData là identify chỉ có data. Nhân viên thấy các thuộc tính trong hồ sơ khách ở hộp thư. Khoá mới được thêm, khoá trùng bị ghi đè, khoá không gửi thì giữ nguyên. Mỗi lần gọi tối đa 8 KB JSON.
pushEvent
$hatang.push(["pushEvent", "viewed_pricing", { plan: "pro" }]);
Trong lúc có hội thoại, sự kiện hiện trong hội thoại đó, để nhân viên biết khách đang làm gì khi chat. Tên tối đa 100 ký tự.
Trước khi khách nhắn, các sự kiện và trang khách xem được giữ làm hành trình của khách: 20 bước gần nhất, trong 24 giờ. Khi khách mở hội thoại (tin nhắn đầu tiên, form offline, lead hay chiến dịch), hành trình được gắn vào hội thoại và hiện cho nhân viên dưới mục Trước khi nhắn trong hội thoại và thanh thông tin khách. Khách không bao giờ thấy. Cùng một trang lặp lại liền nhau chỉ tính một lần. Dữ liệu của mỗi sự kiện được cắt còn 1 KB JSON, và giá trị dưới các khoá trông như bí mật (chứa password, token, secret, card, cvv hoặc otp, không phân biệt hoa thường) được lưu thành [redacted], kể cả trong URL trang. Đừng gửi dữ liệu cá nhân mà bạn không muốn nhân viên thấy.
Widget cũng tự báo khi khách chuyển trang, kể cả khi ứng dụng một trang đổi URL (history.pushState, replaceState và nút quay lại), nên nhân viên thấy trang hiện tại trong Hỗ trợ → Đang truy cập và trong hộp thư.
Sự kiện
Đăng ký bằng on, huỷ bằng off:
function onReply(message) {
console.log("Trả lời mới từ", message.sender_type, message.content);
}
$hatang.push(["on", "message:received", onReply]);
// sau đó
$hatang.push(["off", "message:received", onReply]);
| Sự kiện | Dữ liệu | Khi nào |
|---|---|---|
ready | { visitor_id, conversation_id } | Widget đã khởi động. conversation_id là null nếu khách chưa có hội thoại đang mở. |
open | không | Khung chat mở. |
close | không | Khung chat đóng. |
message:received | { id, sender_type, content } | Nhân viên, trợ lý AI hoặc hệ thống nhắn trong hội thoại của khách. sender_type là agent, bot hoặc system. |
message:sent | { id, sender_type, content } | Tin của khách đã được lưu, kể cả tin gửi từ tab khác. sender_type là visitor. |
conversation:status | { status, conversation_id } | Hội thoại đổi trạng thái: open, pending, resolved hoặc closed. |
unread | { count } | Số tin trả lời chưa đọc thay đổi. Về 0 khi khách mở khung chat. |
conversion | { name, ...data } | Một chuyển đổi được ghi nhận: name là chat_started, lead hoặc purchase. Chỉ phát khi bật theo dõi chuyển đổi. |
ready chỉ phát một lần, ngay sau khi các lệnh trong hàng đợi chạy xong. Đặt on("ready", ...) trước khi script widget tải, ví dụ trong thẻ thứ nhất của đoạn mã, nếu không sẽ lỡ sự kiện này.
Ví dụ: huy hiệu chưa đọc của riêng bạn
<button id="help" data-hatang-open>Hỗ trợ <span id="help-count"></span></button>
<script>
window.$hatang = window.$hatang || [];
window.HATANG_CONFIG = { hideLauncher: true };
$hatang.push(["on", "unread", function (e) {
document.getElementById("help-count").textContent = e.count ? "(" + e.count + ")" : "";
}]);
</script>
Lead từ form của bạn
Bất kỳ form nào trên website cũng tạo được lead. Widget nhận diện khách bằng thông tin trong form, mở hội thoại (hoặc dùng hội thoại đang mở của khách) với một tin tóm tắt form, phân phối theo quy tắc phân phối của bạn và tính một chuyển đổi lead.
document.querySelector("#quote-form").addEventListener("submit", function (e) {
e.preventDefault();
const f = new FormData(e.target);
$hatang.push(["lead", {
name: f.get("name"),
phone: f.get("phone"),
email: f.get("email") || undefined,
message: f.get("message"),
source: "pricing-form",
fields: { city: f.get("city"), budget: f.get("budget") }
}]);
});
| Trường | Kiểu | Giới hạn |
|---|---|---|
name | string | Tối đa 200 ký tự. |
phone | string | Tối đa 40 ký tự. Cần số điện thoại hoặc email. |
email | string | Email hợp lệ, tối đa 320 ký tự. |
message | string | Tối đa 2.000 ký tự. |
source | string | Tối đa 100 ký tự. Hiện trong hội thoại và dùng làm tên chiến dịch trong hệ thống phân tích. Mặc định form. |
fields | object | Chỉ giá trị chuỗi: khoá tối đa 60 ký tự, giá trị tối đa 500. Lưu vào khách truy cập và hồ sơ khách hàng. |
Mỗi lead cũng phát sự kiện webhook chat.campaign.lead.
Không cần widget
Landing page tĩnh có thể gửi cùng JSON thẳng tới chat gateway. Request phải đến từ một trang trên trình duyệt thuộc origin được phép của site; request không có header Origin bị từ chối.
await fetch("https://chat.hatang.dev/widget/v1/sites/site_123.../leads", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ name: "Hà", phone: "0912345678", message: "Báo giá gói Pro", source: "landing-tet" })
});
Gọi thành công trả về 201:
{ "ok": true, "conversation_id": "cnv_123...", "visitor_id": "vis_123...", "customer_id": "cus_123..." }
| Mã | Lỗi | Nguyên nhân |
|---|---|---|
| 403 | origin_not_allowed | Origin của trang không được phép với site. |
| 403 | site_disabled | Site đang tắt. |
| 422 | validation_error | Không có số điện thoại lẫn email, email sai, hoặc một trường vượt giới hạn. |
| 429 | rate_limit_exceeded | Quá 10 lead mỗi phút từ một địa chỉ IP. |
Thêm một ô ẩn tên website vào form và gửi kèm giá trị của nó: đó là honeypot. Người thật để trống; nếu bot điền vào, gateway trả 200 { "ok": true } và không lưu gì.
Theo dõi chuyển đổi
Bật ở Hỗ trợ → Kênh → website của bạn → Tự động hoá → Theo dõi chuyển đổi. Khi đó widget báo ba loại chuyển đổi:
| Chuyển đổi | Khi nào | Sự kiện gtag | Sự kiện dataLayer | Meta Pixel |
|---|---|---|---|---|
chat_started | Khách gửi tin đầu tiên của một hội thoại. | chat_started | hatang_chat_started | Contact |
lead | Khách gửi form của chiến dịch, hoặc trang gọi lead. | generate_lead | hatang_generate_lead | Lead |
purchase | Một đơn hàng trong chat được thanh toán. | purchase | hatang_purchase | Purchase |
- Nếu trang có
gtag, widget gọigtag("event", ...)kèmsource: "hatang_chat". Nếu bạn điền Chuyển đổi Google Ads (send_to), widget gửi thêm sự kiệnconversiontới đích đó. - Nếu không có
gtagnhưng cówindow.dataLayer, widget đẩy sự kiện dataLayer cho Google Tag Manager. - Nếu trang có
fbq, widget ghi sự kiện Meta Pixel. - Trong mọi trường hợp, widget phát sự kiện
conversion.
Điền GA4 measurement id, GA4 API secret, Meta pixel id và Meta Conversions API token để gửi thêm sự kiện phía server (GA4 Measurement Protocol, Meta Conversions API). Các sự kiện này không bị trình chặn quảng cáo chặn và tính cả đơn thanh toán từ Zalo, Messenger, Instagram. Khi khởi động, widget gửi các cookie _ga, _fbp, _fbc để hai phía nối vào cùng một phiên phân tích.
Bước tiếp theo
- Nhận diện khách: ký
identifyđể hộp thư tinuser_id. - SDK React:
<HatangChat />vàuseHatang()bọc API này. - Realtime và giao thức realtime: tự viết client riêng.