# 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](https://app.hatang.dev) with the **Support** module turned on ([Businesses and modules](https://hatang.dev/help/getting-started/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](https://hatang.dev/help/account/employees-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:

```html
<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](https://hatang.dev/docs/guides/support/web-widget/install.md) has steps for each, and the [WordPress plugin](https://hatang.dev/docs/guides/integrations/wordpress.md) needs no code at all.

> **Note: 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:

```html
<script>
  window.$hatang = window.$hatang || [];
  $hatang.push(["identify", {
    user_id: "42",
    email: "an.nguyen@example.com",
    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:

**Node.js**

```js
import { createHmac } from "node:crypto";

export const hatangHash = (userId) =>
  createHmac("sha256", process.env.HATANG_IDENTITY_SECRET).update(String(userId)).digest("hex");
```

**Python**

```python
import os
from hatang import identity_hash  # pip install hatang

def hatang_hash(user_id) -> str:
    return identity_hash(str(user_id), os.environ["HATANG_IDENTITY_SECRET"])
```

**PHP**

```php
$hash = hash_hmac('sha256', (string) $userId, getenv('HATANG_IDENTITY_SECRET'));
```

> **Danger: 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](https://hatang.dev/docs/guides/support/web-widget/identity-verification.md) explains merging, secret rotation and the syntax for every SDK. The [JavaScript API](https://hatang.dev/docs/guides/support/web-widget/javascript-api.md) 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**

```bash
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." }'
```

**Node.js**

```js
import { Hatang } from "@hatang/sdk";

const hatang = new Hatang(process.env.HATANG_API_KEY, { baseUrl: "https://api.hatang.dev" });

const { data: open } = await hatang.chat.conversations.list({ status: "open", limit: 20 });
const conversation = open.data[0];

const { data, error } = await hatang.chat.conversations.send(
  conversation.id,
  { content: "Hi An, your order ships today." },
  { idempotencyKey: `reply-${conversation.id}-1` },
);
```

**Python**

```python
from hatang import Hatang

hatang = Hatang()  # reads HATANG_API_KEY

open_ = hatang.chat.conversations.list(status="open", limit=20)
conversation_id = open_["data"][0]["id"]

message = hatang.chat.conversations.send(
    conversation_id,
    {"content": "Hi An, your order ships today."},
    idempotency_key=f"reply-{conversation_id}-1",
)
print(message)
```

**PHP**

```php
<?php
$client = new GuzzleHttp\Client([
    'base_uri' => 'https://api.hatang.dev',
    'headers' => ['Authorization' => 'Bearer ' . getenv('HATANG_API_KEY')],
]);

$open = json_decode((string) $client->get('/chat/conversations', ['query' => ['status' => 'open', 'limit' => 20]])->getBody(), true);
$conversationId = $open['data'][0]['id'];

$res = $client->post("/chat/conversations/{$conversationId}/messages", [
    'headers' => ['Idempotency-Key' => "reply-{$conversationId}-1"],
    'json' => ['content' => 'Hi An, your order ships today.'],
]);
```

The message call answers `201`:

```json
{ "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](https://hatang.dev/docs/guides/getting-started/webhooks.md) to `chat.message.created`. The [Support REST API guide](https://hatang.dev/docs/guides/support/rest-api.md) 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](https://hatang.dev/docs/guides/sdks/push-notifications.md).

| Platform | Guide |
|---|---|
| Flutter | [Flutter SDK](https://hatang.dev/docs/guides/sdks/flutter.md) |
| iOS (Swift) | [iOS SDK](https://hatang.dev/docs/guides/sdks/ios.md) |
| Android (Kotlin) | [Android SDK](https://hatang.dev/docs/guides/sdks/android.md) |
| React Native / Expo | [React Native SDK](https://hatang.dev/docs/guides/sdks/react-native.md) |
| React / Next.js (web) | [React component](https://hatang.dev/docs/guides/sdks/react.md) |

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

| Symptom | Cause | Fix |
|---|---|---|
| No bubble, `origin_not_allowed` in the browser console | The page is not on the website's domain | Add the origin under **Extra allowed origins** |
| No bubble, `product_disabled` | The Support module is turned off | Ask an owner or admin to turn it on |
| No bubble on some pages | Display rules on the website's page hide it there | Check where the widget is set to show |
| A user shows as unverified | `hash` is missing or does not match | Compute it from the exact user id string, with the current identity secret |
| `invalid_app_key` in a mobile app | Wrong key, or a key of another site | Copy the app key of the right website again |

## Next steps

- [Support overview](https://hatang.dev/docs/guides/support/overview.md): channels, routing, tickets and the AI assistant
- [Widget JavaScript API](https://hatang.dev/docs/guides/support/web-widget/javascript-api.md)
- [Identity verification](https://hatang.dev/docs/guides/support/web-widget/identity-verification.md)
- [SDK overview](https://hatang.dev/docs/guides/sdks/overview.md)

Source: https://hatang.dev/docs/guides/getting-started/quickstart-chat
