Automation
Ha Tang Support can do a lot of the routine work for your team: decide who gets each new conversation, greet and qualify customers with a scripted bot, react to events such as a paid order or a low rating, invite website visitors to chat, nudge customers who went quiet and watch response times. This page explains each tool, where it lives in the dashboard and how to manage it through the API.
| Tool | What it does | Dashboard |
|---|---|---|
| Distribution rules | Pick the agent for a new or handed-off conversation | Support → Distribution rules |
| Flows | A scripted bot that talks to the customer step by step | Support → Automation |
| Workflows | Run steps when an event happens (order paid, rating, visitor waiting…) | Support → Automation → Workflows |
| Campaigns | Proactive messages and lead forms in the website widget | Support → Automation → Campaigns |
| Website automation | Follow-ups, satisfaction ratings, social proof, conversion tracking | Support → Channels → your website → Automation |
| SLA policy | Deadlines for the first reply and the resolution | Support → Tickets → SLA policy |
Before you start
- You need permission to manage Support: an owner, an admin, or an employee with Manage access to Support. See Team and roles.
- Automation works on every channel that lands in the inbox: the website widget, mobile apps, email and the social channels in Channels.
- For API calls, use an API key with the
chat:readscope to read andchat:writeto change anything. See Authentication.
How the pieces fit together
When a customer sends a message, Ha Tang runs these steps in order:
- Blocked customers are checked first. Their messages are kept but go straight to Spam: no routing, no notifications.
- Flows get the first word. If an enabled flow matches the message (or a flow is waiting for the customer's answer), the flow replies.
- If no flow spoke, the AI assistant answers, depending on its mode. See AI assistant.
- When a conversation needs a person (a new conversation, a flow's "Hand to a human" step, an AI hand-off), distribution rules pick the agent.
- In the background, workflows react to events in the conversation, and the minute sweep starts time-based workflows.
When an agent clicks Stop AI in a conversation, flows and the AI assistant both stay silent in that conversation. Flows also never run in a closed conversation or one a human agent already owns.
Distribution rules
Distribution rules decide who gets a new conversation. Open Support → Distribution rules and click Add rule.
Rules run from top to bottom. For each rule whose conditions match, Ha Tang builds a list of candidate agents. The first matching rule that finds an available agent wins. If a rule matches but finds nobody (for example, nobody in its list is online), Ha Tang tries the next rule. If no rule assigns anyone, the conversation stays unassigned until someone picks it up. Conversations that the AI assistant hands off are routed the same way.
Conditions
All conditions you fill in must hold. Leave a condition empty to ignore it.
| Condition | Matches when |
|---|---|
| Channel | The conversation's channel is one of the selected channels (web, zalo, zalo_personal, messenger, instagram, email, tiktok, tiktokshop, whatsapp, telegram, x, line) |
| Tags (any of) | The conversation has at least one of the tags |
| First message contains (any of) | The first message contains one of the words (case-insensitive) |
| Within office hours / Outside office hours | The website's office hours say the team is open or closed |
Rules created in the dashboard apply to every website. Through the API you can also limit a rule to some websites (siteIds) or to specific connected accounts (channelAccountIds).
Who gets the conversation
| Assign by | Behavior |
|---|---|
| Round robin among agents | Takes turns through the candidates |
| Agent with fewest open conversations | Picks the candidate with the fewest open and pending conversations |
| A specific agent | The first agent in the rule's list |
| Leave unassigned (stop here) | Stops evaluating rules and leaves the conversation unassigned |
Candidates are the agents you select in the rule, or the whole team when you leave the list blank. Only employees who can reply in Support count. Three options refine the choice:
- Only assign to agents online now: offline agents are skipped, and the next rule is tried if nobody is left.
- Prefer the customer's owner: a returning customer goes to their owner (set on the customer's profile in Customers) when that person is a candidate.
- Prefer the agent who last talked to this visitor: keeps the customer with the same agent.
Also open a ticket, priority creates one ticket for each conversation the rule assigns, with the priority you pick. This is useful for email: every incoming email becomes a ticket with SLA clocks.
The conversation's info panel in the inbox shows which rule placed it and why. Agents can Re-route a conversation to run the current rules again.
Flows
A flow is a script the bot follows step by step. Use flows to greet customers, ask for a phone number, show a menu of common questions or send product cards before a person or the AI answers. Open Support → Automation and click New flow, or install one of the templates at the bottom of the page. Installed templates start turned off so you can edit the wording first.
Triggers
| Trigger | Starts when |
|---|---|
| First message of a conversation | The customer sends their first message |
| Visitor's message contains a keyword | The message contains one of the keywords (accents and case are ignored) |
| Visitor is on a page | The visitor's current URL contains the text you enter (website widget) |
| Manual / from the inbox only | Never on its own; an agent or the API starts it |
You can narrow every trigger to some channels, to inside or outside office hours, and to visitors who came from a campaign (ref or utm_source in the URL). A flow can apply to one website or to all of them. When several flows match, the first one in the list wins.
Steps
Steps run from top to bottom. Each step has an id that other steps can jump to. A flow has up to 60 steps.
| Step | What it does |
|---|---|
| Send a message | Sends text, optionally with quick reply buttons |
| Ask a question & save the answer | Sends a question, waits for the reply and saves it to the visitor's name, phone or email, a customer field, or a flow variable. It can check that the answer is a phone number or an email address and asks again (up to twice) when it is not. |
| Send product cards | Sends up to 4 products from your catalog, picked by id or by a search (for example {{last}}, the customer's last message) |
| Branch on the answer | Goes to a step when the last answer (or a variable) contains certain words, or when conditions on the conversation or customer hold; otherwise goes to another step |
| Wait | Pauses for 1 to 86,400 seconds |
| Add tags | Adds tags to the conversation |
| Assign to an agent | Gives the conversation to one agent, or to the distribution rules; the flow ends |
| Notify a webhook | Sends a chat.flow.webhook event to your webhooks |
| Jump to a step | Continues at another step |
| Hand to the AI assistant | Ends the flow and lets the AI take over (the assistant must be on) |
| Hand to a human | Optionally says something, then hands the conversation to the distribution rules |
| End | Ends the flow; the next message goes to the AI or an agent as usual |
In message texts, {{name}}, {{phone}} and {{email}} insert the visitor's details, {{last}} the last answer and {{var.order_ref}} a saved variable. Add a fallback after a pipe: {{name|there}}.
While a flow waits on a Wait step, messages from the customer go to whoever answers next (the AI or an agent), not to the flow.
The flow list shows how many times each flow started and completed.
Workflows
A workflow runs a series of steps when something happens in a conversation: an order is paid, a visitor waits too long, a customer leaves a low rating. Open Support → Automation, click Workflows, then New workflow or install a template. Templates are installed turned off so you can edit the messages before turning them on.
Events
| Event | Starts when |
|---|---|
| A conversation starts | A new conversation is created |
| A conversation is resolved | An agent (or automation) resolves it |
| The AI hands off to an agent | The AI assistant hands over |
| A conversation is assigned | Someone is assigned |
| A conversation is tagged | A tag is added; you can limit it to one tag |
| The customer leaves a rating | A satisfaction rating arrives |
| An order is created / An order is paid | An order linked to a conversation is created or paid |
| A lead from a popup | A visitor submits a campaign form |
| A visitor is waiting for a reply | The customer's message has had no reply for N minutes (default 10) |
| An order is still unpaid after a while | An order linked to a conversation is unpaid N minutes after it was created (default 120) |
The two time-based events are checked every minute. To avoid acting on old history when you turn a workflow on, they only pick up conversations and orders that became due within the last hour.
Conditions
Add conditions under Only when and choose whether all or any must hold. A condition compares a field with a value:
| Field | Values |
|---|---|
| Channel | web, zalo, messenger… |
| Conversation status | open, pending, resolved |
| Tag | a tag name |
| Assignee | bot, human, none |
| Office hours | within, outside |
| Has a phone number / Has an email | yes or no |
| Source (utm_source / ref) | for example fb-tet |
| Rating (stars) | for example 3 |
| Order total / Order status | from the order event |
| A customer field | the value of a custom field |
Comparisons: is, is not, contains, greater than, less than, is set, is empty.
Steps
A workflow has up to 30 steps.
| Step | What it does |
|---|---|
| Message the customer | Sends a message in the conversation |
| AI writes a message | The AI assistant writes the message from the conversation, following your instruction; the fallback text is sent when the AI cannot run |
| Email the customer | Sends an email when the customer has an email address; skipped otherwise |
| Send ZNS | Sends a Zalo ZNS template to the customer's phone; skipped when there is no phone number |
| Add / remove tags | Changes the conversation's tags |
| Assign the conversation | Gives it to one agent, or runs the distribution rules again |
| Notify people | Adds people to the conversation and leaves them a private note; nobody selected means the current assignee |
| Create a reminder | Creates a reminder about the conversation in N minutes, for a person or the assignee |
| Change status | Sets the status to open, pending or resolved |
| Send a conversion (GA4/Meta) | Sends a lead or purchase conversion when conversion tracking is on |
| Send a webhook event | Sends a chat.flow.webhook event with source: "workflow" |
| Wait | Waits 1 to 43,200 minutes, then continues |
| Condition | Continues only while the conditions hold; otherwise the run ends |
Messages can use {{name}}, {{phone}}, {{email}}, {{order_code}}, {{order_total}}, {{rating}} and {{site}}.
Runs
Each workflow page lists its Recent runs with the status (Running, Waiting, Done, Stopped, Failed) and a log of what each step did. You can Stop a waiting run or Run again for a conversation. A workflow runs at most once per event. If one workflow starts five times in the same conversation within ten minutes, Ha Tang treats it as a loop and skips further runs. Turning a workflow off stops its runs at their next step.
Campaigns
Campaigns are proactive invitations and lead forms that the website widget shows next to the launcher. Open Support → Automation, click Campaigns, then New campaign. Each campaign belongs to one website.
| Setting | Options |
|---|---|
| Type | Message with buttons (up to 3 buttons; clicking one sends its text as the visitor's first message) or Lead form (name, phone and email fields) |
| Show | Once per visitor, Once per visit or Every page |
| Trigger | After N seconds on the page, or when the visitor scrolls past a percentage, or on exit intent (desktop only). With none of these, the campaign shows shortly after the page loads. |
| Filters | URL contains, URL excludes, referrer contains, UTM source, first visit or returning visitors only, mobile or desktop |
| Only visitors without an open conversation | On by default |
| Only when the team is online | Off by default |
| Content | Text in Vietnamese and English, button labels, submit label and thank-you text |
The widget shows at most one campaign per page, the first in the list whose conditions are met. When a visitor submits a lead form, Ha Tang saves their details, opens a conversation with your thank-you text, routes it with the distribution rules, sends a chat.campaign.lead webhook event and starts any A lead from a popup workflows. The campaign list shows how many times each campaign was shown and clicked, and how many leads it brought.
Website automation
Each website has an Automation tab: Support → Channels, open the website, then Automation.
- Follow-up when the visitor goes quiet: when the team spoke last and the customer has not answered for N minutes (5 to 43,200; default 1,440), send a follow-up, at most 1 to 3 times per conversation. Choose a fixed message, or let the AI write it from the conversation. In AI mode the assistant can also decide to wait longer or stay quiet; it falls back to the fixed message when it cannot run. Conversations outside a channel's reply window are skipped: 48 hours for Zalo OA and TikTok, 24 hours for Messenger, Instagram and WhatsApp.
- Satisfaction rating (CSAT): ask the customer to rate the conversation when it is resolved: stars in the widget, quick replies on Zalo, Messenger and Instagram.
- Social proof toasts: show "Someone just ordered…" toasts next to the launcher, from paid orders of the last 14 days or from a list you write.
- Conversion tracking: send chat started, lead and purchase conversions to the analytics tags already on your page (gtag, Meta Pixel, dataLayer). Add a GA4 measurement id and API secret, or a Meta pixel id and Conversions API token, to also send them server-side, which counts orders from Zalo, Messenger and Instagram too.
SLA policy
The SLA policy sets how fast a ticket must get a first reply and a resolution, per priority. Open Support → Tickets and click SLA policy.
| Priority | First response (default) | Resolution (default) |
|---|---|---|
| Urgent | 15 minutes | 240 minutes (4 hours) |
| High | 60 minutes | 480 minutes (8 hours) |
| Normal | 240 minutes (4 hours) | 1,440 minutes (1 day) |
| Low | 1,440 minutes (1 day) | 4,320 minutes (3 days) |
- Minutes are calendar minutes. Turn on Count business hours only to stop the clock outside the website's office hours and on its holidays.
- A changed policy applies to tickets created from then on, and to existing tickets when their priority changes.
- When a deadline passes, the ticket is marked as breached, the assignee gets a push notification, and Ha Tang sends a
chat.ticket.breachedwebhook event withclockset tofirst_responseorresolve. - New email conversations also get a first-reply deadline from the Normal first response target. The inbox shows "Reply by …" or "Reply overdue" on them.
Related inbox tools
| Tool | Where | Notes |
|---|---|---|
| Canned responses | Support → Canned responses | Type / in the composer to insert one, or pick it from the lightning button in the apps. {{visitor.name|there}} is replaced with the visitor's name. Every agent keeps their own (private) responses, from the dashboard or right in the apps; the team's shared ones are for managers. The shortcut is optional (made from the title) and a response can carry an email subject. |
| Conversation tags | Support → Conversation tags | Each tag has a color and a description. Turn on AI applies it and the assistant adds the tag when a conversation matches the description. |
| Blocked customers | Inbox, ⋯ menu of a conversation | Block a customer, a phone number, an email address or an IP address. Their messages go to Spam. |
Manage automation with the API
All endpoints are on https://api.hatang.dev and need chat:read (GET) or chat:write (everything else).
| Endpoints | Purpose |
|---|---|
GET, POST /chat/routing-rules, PATCH, DELETE /chat/routing-rules/{id}, POST /chat/routing-rules/reorder | Distribution rules |
GET, POST /chat/flows, GET, PATCH, DELETE /chat/flows/{id}, GET /chat/flows/templates | Flows |
POST /chat/conversations/{id}/flows/{flowId} | Start a flow in a conversation now |
GET, POST /chat/workflows, GET, PATCH, DELETE /chat/workflows/{id}, GET /chat/workflows/templates, GET /chat/workflows/{id}/runs | Workflows and their runs |
POST /chat/conversations/{id}/workflows/{workflowId}, POST /chat/conversations/{id}/workflows/stop | Run a workflow now (its trigger conditions are skipped), or stop every running or waiting run in a conversation |
GET, POST /chat/campaigns, GET, PATCH, DELETE /chat/campaigns/{id} | Campaigns |
GET, PUT /chat/sla | SLA policy |
GET, POST /chat/canned-responses, PATCH, DELETE /chat/canned-responses/{id} | Canned responses |
PATCH /chat/flows/{id} and PATCH /chat/workflows/{id} replace the whole definition: send the name, trigger and every step. Routing rules take camelCase fields in requests (agentIds, preferPrevious, onlineOnly) and return snake_case fields (agent_ids, prefer_previous, online_only).
Example: thank customers when an order is paid
- cURL
- Node.js
- Python
- PHP
curl -X POST https://api.hatang.dev/chat/workflows \
-H "Authorization: Bearer ht_123..." \
-H "Content-Type: application/json" \
-d '{
"name": "Thank paid orders",
"trigger": { "event": "order_paid" },
"steps": [
{ "type": "send_message", "text": "Thanks {{name}} for paying order {{order_code}} ({{order_total}})!" },
{ "type": "conversion", "name": "purchase" }
]
}'
// The Node SDK does not wrap workflows yet; call the endpoint directly.
const res = await fetch("https://api.hatang.dev/chat/workflows", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.HATANG_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
name: "Thank paid orders",
trigger: { event: "order_paid" },
steps: [
{ type: "send_message", text: "Thanks {{name}} for paying order {{order_code}} ({{order_total}})!" },
{ type: "conversion", name: "purchase" },
],
}),
});
const workflow = await res.json();
import os, requests
res = requests.post(
"https://api.hatang.dev/chat/workflows",
headers={"Authorization": f"Bearer {os.environ['HATANG_API_KEY']}"},
json={
"name": "Thank paid orders",
"trigger": {"event": "order_paid"},
"steps": [
{"type": "send_message", "text": "Thanks {{name}} for paying order {{order_code}} ({{order_total}})!"},
{"type": "conversion", "name": "purchase"},
],
},
)
workflow = res.json()
<?php
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.hatang.dev']);
$res = $client->post('/chat/workflows', [
'headers' => ['Authorization' => 'Bearer ' . getenv('HATANG_API_KEY')],
'json' => [
'name' => 'Thank paid orders',
'trigger' => ['event' => 'order_paid'],
'steps' => [
['type' => 'send_message', 'text' => 'Thanks {{name}} for paying order {{order_code}} ({{order_total}})!'],
['type' => 'conversion', 'name' => 'purchase'],
],
],
]);
$workflow = json_decode($res->getBody(), true);
Response (201 Created):
{
"object": "chat_workflow",
"id": "cwf_...",
"name": "Thank paid orders",
"site_id": null,
"enabled": true,
"trigger": { "event": "order_paid", "match": "all", "conditions": [] },
"steps": [
{ "type": "send_message", "text": "Thanks {{name}} for paying order {{order_code}} ({{order_total}})!" },
{ "type": "conversion", "name": "purchase" }
],
"started": 0,
"completed": 0,
"created_at": "2026-10-23T03:00:00.000Z",
"updated_at": "2026-10-23T03:00:00.000Z"
}
A request that breaks the schema (an unknown event, a step without its required field, more than 30 steps) returns 422 with a validation_error. See Errors.
Webhook events
| Event | Sent when |
|---|---|
chat.flow.webhook | A flow's Notify a webhook step or a workflow's Send a webhook event step runs |
chat.campaign.lead | A visitor submits a campaign lead form |
chat.ticket.breached | A ticket misses an SLA deadline |
chat.conversation.handoff | The AI assistant hands a conversation to an agent |
See Webhooks for payloads and signatures.
Next steps
- AI assistant: let the AI answer from your knowledge base.
- Zalo ZNS: send order and payment notifications from workflows.
- Widget configuration: office hours and the prechat form.
- REST API for Support: conversations, customers and tickets from your backend.