Skip to main content

Quickstart: add live chat to your site

This guide puts a chat bubble on your website, tells the inbox who your signed-in users are, and has you answer the first message from the dashboard, the Ha Tang mobile apps or your own server. At the end you add the same chat to your iOS, Android, Flutter or React Native app.

Before you start​

  • A Ha Tang account at app.hatang.dev with the Support module turned on (Businesses and modules).
  • A website you can edit, with access to its HTML layout or a tag manager.
  • To change Support settings you need to be an owner or admin, or a member with Manage access to Support (Team and roles).

1. Copy the snippet​

  1. In the dashboard, open Support → Channels and choose your website. To add another one, click Add channel and pick it under Websites.
  2. In the Install card, click Copy snippet.

The snippet is two lines. The first sets your site id (it starts with site_), the second loads the widget from the chat gateway:

<script>window.$hatang=window.$hatang||[];window.HATANG_SITE_ID="site_123...";</script>
<script src="https://chat.hatang.dev/widget.js" async></script>

2. Add it to your site​

Paste the snippet just before </body> on every page, or in the layout that every page shares. The script loads asynchronously and does not block your page.

Reload your site: the chat bubble appears in the corner. Back in the dashboard, the Install card switches to Installed and shows the host it was seen on.

Using WordPress, Shopify, Haravan, LadiPage, Sapo, Google Tag Manager, Webflow or React? The widget install guide has steps for each, and the WordPress plugin needs no code at all.

Where the widget loads

The widget only starts on your website's domain and its subdomains. To test on http://localhost:3000 or a preview deployment, add that origin under Extra allowed origins on the website's page. Other origins are refused with origin_not_allowed.

3. Identify signed-in users​

Without any code, every visitor is an anonymous guest. When a user is signed in on your site, pass their details to the widget so your team sees who they are talking to:

<script>
window.$hatang = window.$hatang || [];
$hatang.push(["identify", {
user_id: "42",
name: "Nguyễn Văn An",
phone: "0901234567",
hash: "<%= hatangHash %>"
}]);
</script>

$hatang.push works before and after the widget has loaded, so you can put this anywhere after the first line of the snippet.

The hash proves that the user id came from your server and not from someone typing it in the browser console. Without it, the details are shown as unverified and are never merged with another customer's history. With a valid hash, the inbox treats the user id as verified and joins all their sessions, on every device, into one customer.

Compute hash on your server as the HMAC-SHA256 of the user id, keyed with the site's Identity secret (on the website's page, Rotate & reveal), in lowercase hex:

import { createHmac } from "node:crypto";

export const hatangHash = (userId) =>
createHmac("sha256", process.env.HATANG_IDENTITY_SECRET).update(String(userId)).digest("hex");
Keep the identity secret on your server

Anyone with the identity secret can sign in to the chat as any of your users. Never put it in page JavaScript, a mobile app or a public plugin; send only the computed hash to the browser.

The identity verification guide explains merging, secret rotation and the syntax for every SDK. The JavaScript API lists the other commands, such as open, setLocale and on.

4. Reply from the inbox​

Open your site in a private window and send a message from the widget. It arrives in Support → Inbox in real time. Type a reply and the visitor sees it at once in the widget.

Your team can also answer from the Ha Tang apps for iPhone, Android and Mac, which receive push notifications for new messages.

To answer from your own systems instead, use the REST API with a key that has the chat:write scope. List open conversations, then post a message as your team:

curl "https://api.hatang.dev/chat/conversations?status=open&limit=20" \
-H "Authorization: Bearer $HATANG_API_KEY"

curl -X POST https://api.hatang.dev/chat/conversations/cnv_123.../messages \
-H "Authorization: Bearer $HATANG_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: reply-cnv_123-1" \
-d '{ "content": "Hi An, your order ships today." }'

The message call answers 201:

{ "object": "chat_message", "id": "msg_123...", "seq": 7, "conversation_id": "cnv_123...", "duplicate": false }

To react to new visitor messages without polling, subscribe a webhook to chat.message.created. The Support REST API guide covers assigning, tagging, private notes, customers and orders.

5. Add chat to your mobile apps​

The native SDKs talk to the same gateway as the widget, so messages from your app land in the same inbox and a verified user keeps one history across web and mobile.

  1. In Support → Channels, click Add channel, then Mobile apps, and Create app key. The key starts with hak_ and is shown only once.
  2. Install the SDK for your platform and configure it with your site id and the app key.
  3. Call identify with the same user id and hash as on the web.
  4. Optionally upload your Firebase credentials so the app receives push notifications.
PlatformGuide
FlutterFlutter SDK
iOS (Swift)iOS SDK
Android (Kotlin)Android SDK
React Native / ExpoReact Native SDK
React / Next.js (web)React component

The app key is not a secret in the way an API key is (it ships inside your app), but you can revoke it and issue a new one at any time. Never ship an ht_ API key or the identity secret in an app.

What can go wrong​

SymptomCauseFix
No bubble, origin_not_allowed in the browser consoleThe page is not on the website's domainAdd the origin under Extra allowed origins
No bubble, product_disabledThe Support module is turned offAsk an owner or admin to turn it on
No bubble on some pagesDisplay rules on the website's page hide it thereCheck where the widget is set to show
A user shows as unverifiedhash is missing or does not matchCompute it from the exact user id string, with the current identity secret
invalid_app_key in a mobile appWrong key, or a key of another siteCopy the app key of the right website again

Next steps​

View this page as Markdown