# AI assistant

The AI assistant answers customers on every channel of your business, using only the knowledge you give it. It can reply on its own, suggest replies for your agents to approve, collect contact details, show products and hand the conversation to a person when it cannot help. This page explains how to set it up, how it decides what to do and how to keep its costs under control.

## Before you start

- You need permission to manage Support (owner, admin, or an employee with **Manage** access to Support).
- The assistant needs an AI provider key. Ha Tang uses the platform key by default; you can add your own key instead (see [Budget and API key](#budget-and-api-key)). When no key is available, the AI assistant page shows a warning.
- Add knowledge before you turn it on. With an empty knowledge base, the assistant hands every conversation to a human.

Everything lives under **Support → AI assistant**, in five tabs: **Assistant**, **Knowledge**, **Situations**, **Tests** and **AI runs**. There is one assistant per business: the same persona, instructions and knowledge serve the website widget, mobile apps, email and every connected social channel.

## Step 1: Add knowledge

Open **Support → AI assistant → Knowledge** (also in the sidebar as **Knowledge**) and add sources. The assistant answers only from what is indexed here.

| Source type | What to enter |
|---|---|
| FAQ (Q/A pairs) | Blocks of `Q: …` and `A: …` (or `Hỏi:` / `Đáp:`) separated by a blank line |
| Text / markdown | Policies, price lists, product details |
| Website, Google Doc / Sheet, or Notion link | A website start URL or `sitemap.xml`; a Google Doc or Sheet shared "anyone with the link"; a Notion page or database shared with your Notion integration |
| PDF / Word file | A PDF or DOCX up to 20 MB |

How sources are processed:

- **Websites** are crawled on the same host, following links two levels deep and up to 50 pages. The crawler honors `robots.txt` and your sitemaps, and skips pages marked `noindex`. Set **Re-crawl every (days)** to refresh it automatically (default 7, `0` = never).
- **Notion** needs an integration token. Create one at notion.so/profile/integrations, paste it in **Support → AI assistant → Assistant** (Team budget and key), then share each page or database with the integration.
- **Files**: text is extracted on upload. A scanned PDF is read by the AI in the background, and that cost counts toward your AI budget.
- Each source shows its status (`pending`, `indexing`, `indexed`, `error`), the number of chunks and when it was last indexed. Click **Re-index** after changing a source outside Ha Tang.

The page header shows how many chunks are indexed and whether semantic search is on. The assistant combines keyword and semantic matches when it looks up an answer.

### Unanswered questions

When the assistant hands a conversation off because the answer was not in the knowledge base, the question lands under **Unanswered questions** on the Knowledge tab. Similar questions are grouped and counted. Click **Answer**, write the right answer and **Save to FAQ**: it becomes a Q/A pair in an FAQ source named "Câu hỏi từ khách", and the assistant uses it from then on. Click **Ignore** for questions you do not want to answer.

### Try it

The **Chat with the AI** panel on the Knowledge tab lets you write as a customer and see how the assistant answers with your current knowledge, situations and products. You can pick the channel and pretend to be inside or outside office hours. Nothing is sent to anyone, but each answer counts toward the AI budget.

## Step 2: Choose a mode

Open the **Assistant** tab and pick a **Mode**:

| Mode | Behavior |
|---|---|
| Off | The assistant never answers. |
| When nobody is online | Answers only while no agent is online; agents take over the moment they reply. |
| First responder | Answers every new customer first and hands off when it cannot help. The widget shows as online. |
| Draft only | Never talks to customers. Suggests a reply to the agent for every customer message. |

Then adjust the rest of the form:

| Setting | Default | Notes |
|---|---|---|
| Assistant name (shown to visitors) | `Trợ lý` | Up to 60 characters |
| Model | Platform default | Leave blank to use the default model |
| Max assistant replies before hand-off | 6 | 1 to 20 per conversation |
| Instructions for the assistant | empty | Tone, what to push, what to avoid; up to 4,000 characters. It still answers only from the knowledge base. |
| Hand-off phrase (vi / en) | "Mình sẽ chuyển bạn tới nhân viên hỗ trợ ngay nhé." / "Let me connect you with a human agent." | Sent when it hands off without writing its own message |
| After a hand-off, keep suggesting replies to the agent | On | Drafts continue after a hand-off |
| Let the assistant ask for the visitor's email when a follow-up is needed | On | Shows an email form in the website widget |
| Save the name and phone number visitors mention | On | Also asks for a phone number when a follow-up would help |

## How the assistant handles a message

[Flows](https://hatang.dev/docs/guides/support/automation.md#flows) get the first word. When no flow replies, the assistant takes its turn:

1. It stays silent when the conversation is closed, a human agent owns it, or an agent clicked **Stop AI**.
2. It checks your [situations](#situations) and applies any that match.
3. After the maximum number of replies, it hands off.
4. It looks up the most relevant knowledge and writes a reply. It can also use these actions:

| Action | When |
|---|---|
| Hand off to a human | The customer asks for a person, complains, wants a refund or cancellation, has a problem with a payment, wants to change personal data, or the answer is not in the knowledge base |
| Save customer details | The customer mentions their name, phone number or email. It also fills custom fields marked **AI fills it in** (**Customers → Custom fields**) while they are empty. |
| Ask for an email | Website conversations only, when a follow-up is needed and the visitor has no email |
| Show products | Your catalog has products; it sends product cards |
| Create a draft order | Your catalog has products; it creates a draft order and leaves a private note asking an agent to confirm it and send a payment link |

When the assistant hands off, it:

- sends the hand-off phrase (or its own message) to the customer;
- leaves a private note for agents with the reason and a short summary;
- passes the conversation to the [distribution rules](https://hatang.dev/docs/guides/support/automation.md#distribution-rules);
- sends a `chat.conversation.handoff` webhook event with the reason (`not_in_knowledge`, `complaint`, `refund`, `payment`, `personal_data`, `requested`, `other`, or a limit such as `max_turns`);
- falls back to the offline flow when nobody on the team is online.

If the customer comes back after the conversation was resolved, the assistant starts answering again.

## Suggested replies (drafts)

In **Draft only** mode, and after a hand-off when **keep suggesting replies** is on, the assistant writes a suggestion for each customer message instead of replying. The suggestion appears above the composer as **AI suggestion** with two buttons:

- **Insert** puts the text in the composer so you can edit and send it.
- **Discard** dismisses it.

Drafts are only written while at least one agent is online. When the knowledge base does not cover the question, the draft says honestly that the team will check and follow up.

## In the inbox

- **Stop AI** silences the assistant (and flows) in one conversation; **Resume AI** gives the conversation back to it.
- Under each AI reply, **Knowledge used** shows the chunks it relied on. **Improve this reply** lets you correct the question and answer; the pair is saved to the "Câu hỏi từ khách" FAQ and used next time.

## Situations

Situations teach the assistant how to handle specific cases. Open the **Situations** tab, click **Add situation** or start from a template. You can have up to 10; situations higher in the list win.

| Field | Options |
|---|---|
| Starts when | **The visitor talks about a topic** (the AI recognizes it from your description), **The message contains keywords** (accents ignored), or **Every message** |
| Conditions | Office hours (any, within, outside), channels, websites |
| Instructions for the AI | How to reply in this situation (up to 1,000 characters) |
| Extra context | Facts the AI needs only here, such as wholesale prices (up to 2,000 characters) |
| Then | **Just reply as instructed**, **Bring in an agent to follow along (the AI keeps replying)**, **Hand off to an agent**, **Stop the AI in this conversation**, **Mark as spam and stop the AI** |
| Apply at most once per conversation | On by default |

> **Tip**
>
> Keep the list short. Too many rules make the assistant rigid and harder to predict. Put general guidance in the instructions on the Assistant tab and use situations for real exceptions.

## Tests

The **Tests** tab checks the assistant against questions your customers really ask. For each test you write the customer's message, optional earlier chat, and what a good reply does. Each test is answered 3 times and graded 0 to 100. A large spread between the three answers means the assistant is not consistent yet. Each grading counts toward the AI budget.

## AI runs

The **AI runs** tab lists every model call the assistant made in the last 30 days: when, what kind (reply, draft, follow-up, workflow, comment, tagging, test…), the outcome, the conversation, tokens, cost and latency. The Assistant tab sums up the last 30 days: runs, hand-offs and cost.

## Budget and API key

The **Team budget and key** card on the Assistant tab sets:

- **Daily cap** and **Monthly cap** in USD. Leave them blank to use the platform defaults. When a cap is hit, the assistant stops answering, admins get one email per day, and customers fall back to the offline flow.
- **Your own AI provider API key**, for the provider of the configured model. Without it, the platform key is used. **Remove the team key** switches back.
- The **Notion integration token** for Notion knowledge sources.

Everything the AI does for your business counts toward the same caps: replies, drafts, AI follow-ups, workflow **AI writes a message** steps, AI comment replies, automatic tagging, OCR of scanned PDFs, tests and the try-it panel.

## Other places the AI helps

- **Follow-ups**: a website's Automation tab can let the AI write follow-ups to customers who went quiet. See [Automation](https://hatang.dev/docs/guides/support/automation.md#website-automation).
- **Workflows**: the **AI writes a message** step. See [Automation](https://hatang.dev/docs/guides/support/automation.md#workflows).
- **Comment auto-replies**: on Facebook, Instagram, TikTok and X accounts, public replies and private messages can be written by the AI. See the channel guides, for example [Messenger and Instagram](https://hatang.dev/help/channels/messenger-instagram).
- **Tags**: tags marked **AI applies it** in **Support → Conversation tags** are added automatically when a conversation matches their description, a few minutes after the first message and when it is resolved. The AI only adds tags; agents remove the ones they disagree with.

## Manage knowledge with the API

To push your own documents (a help center, a CMS, product docs) under your own ids and keep them in sync on every deploy, use the [Knowledge API](https://hatang.dev/docs/guides/support/knowledge-api.md). The source endpoints below add FAQs, text, files and websites to crawl.

Knowledge belongs to the business. The endpoints include a website id (`site_…`) in the path; any of your websites works.

| Endpoint | Purpose |
|---|---|
| `GET /chat/sites/{id}/kb/sources` | List knowledge sources |
| `POST /chat/sites/{id}/kb/sources` | Add an FAQ, text or URL source; indexing runs in the background |
| `POST /chat/sites/{id}/kb/files` | Upload a PDF or DOCX (multipart field `file`, optional `title`), up to 20 MB |
| `GET, PATCH, DELETE /chat/sites/{id}/kb/sources/{sourceId}` | Read, update (re-indexes) or delete a source |
| `POST /chat/sites/{id}/kb/sources/{sourceId}/reindex` | Re-index a source |
| `GET /chat/sites/{id}/kb/search?q=…&limit=6` | Search the knowledge the assistant answers from (`limit` 1 to 20) |

Reads need `chat:read`; changes need `chat:write`. Request fields for a source: `kind` (`faq`, `text` or `url`), `title`, `content` (for FAQ and text, up to 200,000 characters), `url` (for `url`) and `refresh_days` (0 to 90, default 7).

**cURL**

```bash
curl -X POST https://api.hatang.dev/chat/sites/site_123/kb/sources \
  -H "Authorization: Bearer ht_123..." \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "faq",
    "title": "Shipping",
    "content": "Q: How long does delivery take?\nA: 1-3 days in the city, 3-5 days elsewhere.\n\nQ: Is shipping free?\nA: Free for orders from 500,000 VND."
  }'
```

**Node.js**

```js
// The Node SDK does not wrap knowledge sources yet; call the endpoint directly.
const res = await fetch("https://api.hatang.dev/chat/sites/site_123/kb/sources", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.HATANG_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    kind: "faq",
    title: "Shipping",
    content: "Q: How long does delivery take?\nA: 1-3 days in the city, 3-5 days elsewhere.\n\nQ: Is shipping free?\nA: Free for orders from 500,000 VND.",
  }),
});
const source = await res.json();
```

**Python**

```python
import os, requests

res = requests.post(
    "https://api.hatang.dev/chat/sites/site_123/kb/sources",
    headers={"Authorization": f"Bearer {os.environ['HATANG_API_KEY']}"},
    json={
        "kind": "faq",
        "title": "Shipping",
        "content": "Q: How long does delivery take?\nA: 1-3 days in the city, 3-5 days elsewhere.\n\n"
                   "Q: Is shipping free?\nA: Free for orders from 500,000 VND.",
    },
)
source = res.json()
```

**PHP**

```php
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.hatang.dev']);
$res = $client->post('/chat/sites/site_123/kb/sources', [
    'headers' => ['Authorization' => 'Bearer ' . getenv('HATANG_API_KEY')],
    'json' => [
        'kind' => 'faq',
        'title' => 'Shipping',
        'content' => "Q: How long does delivery take?\nA: 1-3 days in the city, 3-5 days elsewhere.\n\nQ: Is shipping free?\nA: Free for orders from 500,000 VND.",
    ],
]);
$source = json_decode($res->getBody(), true);
```

Response (`201 Created`):

```json
{
  "object": "chat_kb_source",
  "id": "kbs_...",
  "site_id": "site_123",
  "kind": "faq",
  "title": "Shipping",
  "url": null,
  "status": "pending",
  "error": null,
  "pages": 0,
  "chunk_count": 0,
  "refresh_days": 7,
  "last_indexed_at": null,
  "created_at": "2026-10-23T03:00:00.000Z"
}
```

Poll `GET /chat/sites/{id}/kb/sources/{sourceId}` until `status` is `indexed` (or `error`, with the reason in `error`). A `url` source without `url`, or an FAQ or text source without `content`, returns `422 validation_error`.

Agents' apps can also stop or resume the assistant in a conversation and settle drafts through the agent API of the chat gateway. See [Chat gateway](https://hatang.dev/docs/reference/chat-gateway.md).

## Troubleshooting

| Symptom | Cause | Fix |
|---|---|---|
| The assistant hands every conversation off | The knowledge base is empty, or nothing is indexed yet | Add sources and wait for `indexed` |
| It never answers | Mode is **Off**, an agent stopped it in that conversation, a human owns the conversation, or the mode is **When nobody is online** and an agent is online | Check the mode and the conversation's assignee; click **Resume AI** |
| No drafts appear | No agent is online, the knowledge base is empty, or the mode is not **Draft only** and there was no hand-off | Drafts need an online agent and some indexed knowledge |
| It stopped answering everywhere | The daily or monthly cap was hit | Raise the caps or wait for the next day or month; see **AI runs** for what used the budget |
| A website source shows `error` | The site blocks the crawler in `robots.txt`, or the URL is unreachable | Allow the crawler, or add the content as text or a file |

## Next steps

- [Automation](https://hatang.dev/docs/guides/support/automation.md): flows, workflows and distribution rules that work with the assistant.
- [Webhooks reference](https://hatang.dev/docs/reference/webhooks.md): react to `chat.conversation.handoff` in your own systems.
- [REST API for Support](https://hatang.dev/docs/guides/support/rest-api.md): read conversations and messages from your backend.

Source: https://hatang.dev/docs/guides/support/ai-assistant
