Chuyển tới nội dung chính

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.push chạ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ệnhTham sốTác dụng
openkhôngMở khung chat.
closekhôngĐóng khung chat.
togglekhôngMở khung nếu đang đóng, đóng nếu đang mở.
hidekhôngẨn nút chat. Khung đang mở vẫn mở.
showkhôngHiệ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.
pushEventname, { ...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.
unreadCountcallback(count)Gọi callback với số tin trả lời chưa đọc hiện tại.
onevent, callbackĐăng ký nghe một sự kiện. Xem Sự kiện.
offevent, callbackHuỷ callback đã đăng ký bằng on. Truyền đúng hàm đó.
resetkhôngQuê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ườngKiểuGiới hạn
user_idstringTối đa 200 ký tự. ID người dùng trong hệ thống của bạn.
emailstringEmail hợp lệ, tối đa 320 ký tự.
namestringTối đa 200 ký tự.
phonestringTối đa 40 ký tự.
hashstringHMAC-SHA256(identity secret, user_id) dạng hex, tính ở server.
dataobjectThuộ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.

cẩn thận

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ệnDữ liệuKhi 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ở.
openkhôngKhung chat mở.
closekhôngKhung 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.
mẹo

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ườngKiểuGiới hạn
namestringTối đa 200 ký tự.
phonestringTối đa 40 ký tự. Cần số điện thoại hoặc email.
emailstringEmail hợp lệ, tối đa 320 ký tự.
messagestringTối đa 2.000 ký tự.
sourcestringTố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.
fieldsobjectChỉ 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ỗiNguyên nhân
403origin_not_allowedOrigin của trang không được phép với site.
403site_disabledSite đang tắt.
422validation_errorKhông có số điện thoại lẫn email, email sai, hoặc một trường vượt giới hạn.
429rate_limit_exceededQuá 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 đổiKhi nàoSự kiện gtagSự kiện dataLayerMeta Pixel
chat_startedKhách gửi tin đầu tiên của một hội thoại.chat_startedhatang_chat_startedContact
leadKhách gửi form của chiến dịch, hoặc trang gọi lead.generate_leadhatang_generate_leadLead
purchaseMột đơn hàng trong chat được thanh toán.purchasehatang_purchasePurchase
  • Nếu trang có gtag, widget gọi gtag("event", ...) kèm source: "hatang_chat". Nếu bạn điền Chuyển đổi Google Ads (send_to), widget gửi thêm sự kiện conversion tới đích đó.
  • Nếu không có gtag như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​

Xem trang này dạng Markdown