Giao thức realtime của chat (v1)
Mọi client chat (widget web và các SDK Flutter, React Native, iOS, Android) nói cùng một giao thức WebSocket với gateway chat.hatang.dev. Đọc trang này nếu bạn muốn tự viết client hoặc muốn hiểu SDK làm gì bên dưới. Các bảng khung ở phần sau được sinh từ JSON Schema đi kèm gói @hatang/chat-protocol, nên luôn khớp với gateway.
Bản máy đọc được: chat.hatang.dev/protocol.json (phiên bản, giới hạn, mã đóng, mã lỗi) và các tệp JSON Schema visitor-frames.json, server-frames.json, http.json, protocol.json. Những gì một SDK native phải làm trên nền giao thức này (vòng đời phiên, lịch sử, đính kèm, thông báo đẩy) được kiểm bằng bộ kịch bản tuân thủ C01–C12 mà mọi SDK phải chạy qua trước khi phát hành.
Phong bì
- Gọi
POST /widget/v1/sites/{siteId}/bootstrap. Lệnh này tạo hoặc làm mới token khách và trả vềws_url. Trình duyệt cần Origin được phép cho site; ứng dụng native thì gửi khoá ứng dụng trong headerx-hatang-app-key, và token trả về mang id của ứng dụng. - Mở
ws_url:wss://chat.hatang.dev/ws?site=<site_id>, thêm&app=<app_id>&install=<install_id>với ứng dụng. Dùng đúng như được trả về; socket của ứng dụng không cần Origin. - Gửi
authlàm khung đầu tiên, trong vòng 5 giây.
Mỗi khung là một object JSON:
{ "t": "message.send", "id": "req_1", "v": 1, "d": { "client_id": "c_9f2", "content": "Shop còn hàng không ạ?" } }
| Khoá | Ý nghĩa |
|---|---|
t | Loại khung |
d | Dữ liệu |
v | Phiên bản giao thức (1) |
id | Id yêu cầu, tuỳ chọn; được trả lại trong message.ack hoặc error.for để bạn ghép câu trả lời với yêu cầu |
Cả hai phía gửi ping mỗi 25 giây và trả lời bằng pong. Với các endpoint HTTP, gửi token khách trong header x-visitor-token. Trong khung identify, trường user id tên là user_id; mọi SDK native gửi đúng tên này, còn API $hatang của widget web nhận cả userId và chuyển thành user_id.
Thứ tự và resume
Mọi tin nhắn và sự kiện trong một hội thoại mang seq tăng dần từng đơn vị. Lưu seq lớn nhất đã thấy. Sau khi kết nối lại (hoặc ngay sau ready nếu đã có seq), gửi resume { conversation_id, after_seq }; server phát lại mọi thứ sau đó từ cơ sở dữ liệu, nên khung bị lỡ lúc socket đứt không bao giờ mất hẳn. Tải lịch sử cũ hơn qua HTTP với before_seq, và bắt kịp với after_seq.
Gửi một tin:
- Sinh
client_idcho tin và hiện bong bóng ở trạng thái đang gửi. - Gửi
message.sendvà chờmessage.acktối đa 10 giây. - Hết hạn hoặc gặp lỗi thì giữ bong bóng ở trạng thái lỗi và thử lại với cùng
client_id. Server trả lờiclient_idtrùng bằng tin gốc thay vì tạo tin thứ hai. - Khung
messagecủa chính tin bạn gửi cũng đến; dedupe theoid.
Mã đóng
| Mã | Ý nghĩa | Client làm gì |
|---|---|---|
| 4001 | Không có auth trong 5 giây | Kết nối lại, gửi auth trước |
| 4003 | Xác thực bị từ chối hoặc phiên bị thu hồi (ví dụ khoá ứng dụng bị thu hồi) | Dừng; bỏ token, bootstrap một khách mới |
| 4004 | Giao thức của client cũ hơn mức gateway hỗ trợ; khung reload đến trước | Trình duyệt tải lại trang; client native dừng kết nối lại và báo người dùng cập nhật ứng dụng |
| 4005 | Khung session vừa mang token khách mới (gộp danh tính) | Kết nối lại ngay với token mới |
| khác | Lỗi mạng, gateway khởi động lại | Kết nối lại với backoff min(30 s, 500 ms · 2^n) cộng jitter tối đa 500 ms |
Mã lỗi
Lỗi HTTP có dạng { statusCode, name, message }; lỗi socket là khung error { code, message, for? }.
| Mã | HTTP | Ý nghĩa |
|---|---|---|
invalid_app_key | 401 | Header x-hatang-app-key thiếu, sai hoặc thuộc site khác |
app_key_revoked | 403 | Khoá đã bị thu hồi trong dashboard; ứng dụng cần bản cập nhật với khoá mới |
invalid_visitor_token | 401 | Token khách hết hạn, bị reset hoặc bị thu hồi; bootstrap lại |
unauthenticated | 401 | Thiếu token |
origin_not_allowed | 403 | Origin của trình duyệt không được phép cho site này |
site_disabled | 403 | Site đã bị tắt |
product_disabled | 403 | Doanh nghiệp đang tắt module Hỗ trợ khách hàng (Doanh nghiệp và module). Widget ẩn nút chat; SDK báo lỗi khi khởi tạo chat còn phần còn lại của ứng dụng vẫn chạy. Bật lại module là kết nối lại được |
validation_error | 422 | Dữ liệu sai dạng hoặc vượt giới hạn |
rate_limit_exceeded | 429 | Xem giới hạn tần suất bên dưới; thử lại sau |
conversation_closed | 409 | Gửi vào hội thoại đã đóng |
file_too_large, unsupported_file_type | 413, 422 | Tải tệp: tối đa 10 MB; ảnh (kể cả HEIC), PDF, tệp văn phòng, zip, mp4 và m4a |
not_found | 404 | Hội thoại, tin hoặc tệp không thuộc khách này |
bad_frame | — | Khung không phải JSON hợp lệ hoặc thiếu t |
Giới hạn tần suất:
| Loại | Giới hạn |
|---|---|
| Bootstrap | 30 lần / phút cho mỗi lượt cài ứng dụng, hoặc mỗi địa chỉ IP với trình duyệt |
| Socket mở cùng lúc | 3 cho mỗi lượt cài ứng dụng |
| Tin của khách | 5 tin / 10 giây và 60 tin / phút cho mỗi khách |
| Tải tệp | 10 lần / phút cho mỗi khách |
| Đăng ký thiết bị | 10 lần / phút cho mỗi khách |
Vẽ thẻ
Tin có metadata.kind là product, order, payment, quick_replies hoặc csat mang dữ liệu có cấu trúc để client vẽ thành thẻ: sản phẩm, đơn hàng, nút thanh toán, lựa chọn nhanh hoặc lời mời đánh giá. content của tin luôn là chữ thuần, nên kênh không có thẻ vẫn đọc được. Bấm một lựa chọn nhanh là gửi nhãn đó như tin của khách. csat mời khách đánh giá (sao trên widget, lựa chọn nhanh ở nơi khác), và client trả lời bằng khung rate. Kind lạ hiện như tin thường. Dạng của từng thẻ ở mục Thẻ trong metadata của tin nhắn.
Khi tự viết client:
- Chữ phía trên thẻ. Với thẻ
product,ordervàpayment,contentlà bản tóm tắt đầy đủ dành cho email, thông báo đẩy và các kênh khác; đừng hiện nó trong bong bóng cạnh thẻ. Chỉ hiện câu dẫnmetadata.intronếu có, không có thì bỏ bong bóng.quick_replies,csatvà kind lạ vẫn hiệncontent. - Mã VietQR.
qr_image_urllà ảnh SVG (/pay/{id}/qr.svg). Trình duyệt vẽ được, nhưng nhiều thư viện ảnh di động thì không. Hãy tự vẽ mã từqr_payload(chuỗi EMVCo); thẻ cũ không có trường này thì đọcqr.payloadtừGET /pay/{id}công khai. - Đã thanh toán. Tin chứa thẻ thanh toán không được cập nhật khi khách trả tiền. Coi thẻ là đã thanh toán khi
statuslàpaidhoặcsucceeded, khi dòng thời gian có tinmetadata.kind = "order_paid"cùngorder_id, hoặc khi có sự kiệnorder_paidvớidata.payment_idhaydata.order_idkhớp. Khi đó ẩn mã QR, nội dung chuyển khoản và nút thanh toán. - Ứng dụng bán hàng số. Trên iOS và Android, hàng số (khoá học, gói hội viên…) phải bán qua in-app purchase. Các SDK di động có hook
HatangCommerceđể chuyển nút mua sang mã in-app purchase của bạn; hàng vật lý vẫn dùng VietQR.
Endpoint HTTP
Mọi đường dẫn dưới đây nằm sau /widget/v1/sites/{siteId}/. OpenAPI đầy đủ ở Tham chiếu gateway chat.
| Endpoint | Tác dụng |
|---|---|
POST bootstrap | Tạo hoặc làm mới token khách. Trả về visitor, conversation, timeline, availability, ws_url và cấu hình site. SDK native gửi app { install_id, version, sdk, os, os_version, device_model, locale } và screen |
GET conversations?status=all|open|resolved&limit&after | Các hội thoại của khách, kể cả đã giải quyết, cho hộp thư trong ứng dụng |
POST conversations | Trả về hội thoại đang mở của khách, hoặc mở hội thoại mới |
GET conversations/{id}/messages?before_seq&limit hoặc ?after_seq | Một trang lịch sử cũ hơn, hoặc phần bắt kịp |
POST conversations/{id}/messages | Gửi tin qua HTTP khi không có socket |
POST uploads | Multipart file; trả về { id, name, size, type }. Sau đó gửi message.send { content: "", attachments: [id] } |
PUT devices, DELETE devices | Đăng ký hoặc gỡ token đẩy (Thông báo đẩy) |
POST leads, POST offline-message | Lead từ một form; tin nhắn để lại ngoài giờ làm việc |
POST reset | Quên khách; token cũ ngừng hoạt động và một token mới được trả về |
POST sessions/restore | Đổi token trong link email "chúng tôi đã trả lời" (/c/<token>) lấy phiên của khách đó |
URL tệp đính kèm trong attachments[].url là link có chữ ký tới GET /files/{id}?exp&sig, dùng được trong 24 giờ. Khi link hết hạn, tải lại tin để có link mới.
Giới hạn và thời gian
Phiên bản giao thức: 1.
| Giới hạn | Giá trị |
|---|---|
| Kích thước một khung | 16384 B |
| Tin nhắn của khách | 4000 ký tự |
| Tin nhắn của nhân viên | 10000 ký tự |
| Tệp đính kèm mỗi tin | 5 |
| Kích thước một tệp | 10 MB |
Dữ liệu tuỳ chỉnh (identify.data, setData) | 8 KB |
Hạn gửi auth sau khi mở socket | 5 giây |
Chu kỳ ping (cả hai phía) | 25 giây |
Chờ message.ack / trả lời một yêu cầu | 10 giây |
| Kết nối lại | min(30 s, 500 ms · 2^n) + jitter ≤ 500 ms; ngay lập tức sau mã 4005 |
Mã đóng socket: 4001 auth_timeout · 4003 auth_failed · 4004 protocol_outdated · 4005 session_replaced.
Mã lỗi: validation_error, rate_limit_exceeded, not_found, unauthenticated, invalid_visitor_token, origin_not_allowed, site_disabled, invalid_app_key, app_key_revoked, conversation_closed, file_too_large, unsupported_file_type, bad_frame, error.
Khung visitor → server
auth
Khung đầu tiên, trong 5 giây sau khi mở socket. Trình duyệt gửi page; SDK native gửi screen, app và client. app_state cho phép socket mở từ nền không bị tính là đang online.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
token | string | có | ≤ 2048 ký tự |
site_id | string | có | 1–64 ký tự |
page | object {url, title?, referrer?} | không | |
screen | object {name, params?} | không | |
app | object {install_id, version?, sdk?, os?, os_version?, device_model?, locale?} | không | |
client | object {sdk, version} | không | |
locale | string | không | ≤ 10 ký tự |
app_state | object {foreground} | không |
page
Khách chuyển trang (trình duyệt); ghi vào hội thoại đang mở, hoặc vào hành trình trước khi nhắn.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
url | string | có | ≤ 2048 ký tự |
title | string | không | ≤ 300 ký tự |
referrer | string | không | ≤ 2048 ký tự |
screen
Khách chuyển màn hình (SDK native); thay cho page, và được ghi như page.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
name | string | có | 1–200 ký tự |
params | object<string, string> | không | khoá ≤ 60, giá trị ≤ 500 ký tự |
app.state
Ứng dụng vào nền hoặc trở lại. Ứng dụng ở nền được coi là offline: trả lời đi qua thông báo đẩy hoặc email; màn hình đang mở (conversation_id) không nhận đẩy.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
foreground | boolean | có | |
conversation_id | string | null | không | 1–64 ký tự |
message.send
Gửi tin. client_id do client sinh, dùng lại khi thử lại; server dedupe. content rỗng cần ít nhất một attachments. Giới hạn 5 tin / 10 giây và 60 tin / phút.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
conversation_id | string | không | 1–64 ký tự |
client_id | string | có | 1–64 ký tự |
content | string | có | ≤ 4000 ký tự |
attachments | string[] | không | ≤ 5 phần tử; mỗi phần tử ≤ 64 ký tự |
typing
Khách đang gõ.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
conversation_id | string | có | 1–64 ký tự |
on | boolean | có |
read
Khách đã đọc tới up_to_seq; chỉ gửi khi hội thoại đang hiển thị và ứng dụng ở tiền cảnh.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
conversation_id | string | có | 1–64 ký tự |
up_to_seq | integer | có | ≥ 0; ≤ 2147483647 |
identify
Gắn danh tính. hash là HMAC-SHA256 hex của user_id với identity secret của site (xem Nhận diện khách). Danh tính có chữ ký đã thuộc khách khác sẽ được gộp và trả về khung session.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
user_id | string | không | ≤ 200 ký tự |
email | string | không | ≤ 320 ký tự; khớp ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$; email |
name | string | không | ≤ 200 ký tự |
phone | string | không | ≤ 40 ký tự |
hash | string | không | ≤ 128 ký tự |
data | object | không |
event
Sự kiện tuỳ ý từ ứng dụng (pushEvent), ghi vào hội thoại đang mở. Khi khách chưa có hội thoại, các trang, màn hình và sự kiện được giữ làm hành trình của khách (20 bước gần nhất, trong 24 giờ) và gắn vào hội thoại khách mở, chỉ nhân viên thấy. Giá trị dưới khoá trông như bí mật (password, token, secret, card, cvv, otp) được thay bằng [redacted].
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
name | string | có | ≤ 100 ký tự |
data | object | không |
resume
Phát lại các mục sau after_seq của một hội thoại thuộc khách, sau khi kết nối lại.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
conversation_id | string | có | 1–64 ký tự |
after_seq | integer | có | ≥ 0; ≤ 2147483647 |
rate
Đánh giá hội thoại (1–5) sau khi được giải quyết.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
conversation_id | string | có | 1–64 ký tự |
score | integer | có | ≥ 1; ≤ 5 |
comment | string | không | ≤ 1000 ký tự |
campaign
Tương tác với chiến dịch (đã hiện, bấm, mở, gửi form).
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
campaign_id | string | có | 1–64 ký tự |
action | "shown" | "clicked" | "open" | "submit" | có | |
button | string | không | ≤ 40 ký tự |
fields | object {name?, phone?, email?} | không |
ping
Giữ kết nối, mỗi 25 giây; server trả pong.
id: tuỳ chọn (đối chiếu với message.ack / error).
Không có dữ liệu (d là null).
Khung server → visitor
ready
Xác thực xong: khách, hội thoại đang mở (hoặc null), 100 mục gần nhất và trạng thái trực tuyến của đội.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
visitor | Visitor (Visitor) | có | |
conversation | Conversation | null (Conversation) | có | |
timeline | (Message | Event)[] (Message, Event) | có | |
timeline_has_more | boolean | không | |
availability | Availability (Availability) | có |
message
Tin nhắn mới trong hội thoại (kể cả bản echo tin của chính khách; dedupe theo id).
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
id | string | có | 1–64 ký tự |
conversation_id | string | có | 1–64 ký tự |
seq | integer | có | |
sender_type | "visitor" | "agent" | "bot" | "system" | có | |
sender_id | string | null | có | |
sender_name | string | null | không | |
visibility | "public" | "private" | có | |
content_type | "text" | "file" | có | |
content | string | có | |
attachments | Attachment[] (Attachment) | có | |
client_id | string | null | không | |
metadata | object | không | |
delivery_status | "pending" | "sent" | "failed" | null | không | |
delivery_error | string | null | không | |
read_at | string | null | không | |
created_at | string | có |
message.ack
Xác nhận message.send với id, seq và conversation_id thật.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
client_id | string | có | |
id | string | có | 1–64 ký tự |
seq | integer | có | |
conversation_id | string | có | 1–64 ký tự |
event
Sự kiện trong hội thoại. Khách chỉ nhận status_changed, handoff, bot_resumed, rated, order_paid, ticket_created, ticket_status, notice; bỏ qua loại lạ.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
id | string | có | 1–64 ký tự |
conversation_id | string | có | 1–64 ký tự |
seq | integer | có | |
type | string | có | |
actor_type | "visitor" | "agent" | "bot" | "system" | có | |
actor_id | string | null | có | |
data | object | có | |
created_at | string | có |
typing
Nhân viên đang gõ.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
conversation_id | string | có | 1–64 ký tự |
side | "visitor" | "agent" | có | |
on | boolean | có | |
name | string | không |
availability
Đội có trực tuyến không, có trong giờ làm việc không, giờ mở lại và số nhân viên online.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
online | boolean | có | |
within_hours | boolean | có | |
next_open_at | string | null | có | |
agents_online | integer | có |
read
Bên kia đã đọc tới up_to_seq.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
conversation_id | string | có | 1–64 ký tự |
side | "visitor" | "agent" | có | |
up_to_seq | integer | có |
conversation.update
Một hội thoại của khách đổi trạng thái, số chưa đọc hoặc người phụ trách (view an toàn cho khách).
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
id | string | có | 1–64 ký tự |
site_id | string | có | 1–64 ký tự |
status | "open" | "pending" | "resolved" | "closed" | có | |
seq | integer | có | |
visitor_unread | integer | có | |
last_message_at | string | null | có | |
last_message_preview | string | null | có | |
last_message_sender_type | "visitor" | "agent" | "bot" | "system" | null | có | |
first_response_at | string | null | có | |
resolved_at | string | null | có | |
subject | string | null | có | |
created_at | string | có | |
updated_at | string | có | |
agent | object {name, avatar_url} | null | có |
resume
Trả lời resume: các mục sau after_seq, has_more khi còn tiếp.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
conversation_id | string | có | 1–64 ký tự |
items | (Message | Event)[] (Message, Event) | có | |
has_more | boolean | có |
conversation.start
Đội chủ động nhắn trước; open cho biết có nên mở màn hình chat ngay không.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
conversation | Conversation (Conversation) | có | |
open | boolean | có |
session
Phiên đổi chủ (gộp danh tính hoặc reset): lưu visitor_token mới, bootstrap lại. Socket web nhận reload ngay sau; socket ứng dụng bị đóng với mã 4005.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
visitor_token | string | có | |
visitor_id | string | có | 1–64 ký tự |
reason | "identity_merged" | "reset" | có |
identified
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
visitor_id | string | có | 1–64 ký tự |
verified | boolean | có |
reload
Client web tải lại trang; client native bootstrap lại (kèm mã đóng 4004 thì dừng kết nối lại và báo cần cập nhật ứng dụng).
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
reason | string | có |
error
Lỗi cho một yêu cầu (for là id của khung gây lỗi) hoặc lỗi chung.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
code | string | có | |
message | string | có | |
for | string | không |
pong
Trả lời ping.
id: tuỳ chọn (đối chiếu với message.ack / error).
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
now | string | có |
HTTP
Phản hồi POST bootstrap
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
visitor_token | string | có | |
visitor | Visitor (Visitor) | có | |
config | object {site_id, name, color, position, locale_default, greeting, away_message, offline_capture, hide_when_offline, theme, bot_enabled, agent_avatars, launcher?, header?, greeting_options?, contact_buttons?, layout?, prechat?, visibility?, notifications?, allow_in_iframe?} | có | |
availability | Availability (Availability) | có | |
conversation | Conversation | null (Conversation) | có | |
timeline | (Message | Event)[] (Message, Event) | có | |
timeline_has_more | boolean | không | |
bundle | string | có | |
ws_url | string | có | |
protocol_version | integer | có | |
app | object {id, platform} | null | có |
Thẻ trong metadata của tin nhắn
Một tin có metadata.kind thuộc danh sách dưới mang dữ liệu có cấu trúc; content luôn là bản chữ thuần để email, xem trước và các kênh không có mẫu vẫn đọc được. Kind lạ coi như tin thường.
product
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
kind | "product" | có | |
products | (object {id, name, price, compare_at_price?, currency, image_url?, url?, sku?, store_product_ids?, base_plan_id?, offer_id?, billing_period?, trial_days?})[] | có | |
intro | string | không |
order
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
kind | "order" | có | |
order | object {id, code, status, items, subtotal, discount, shipping, total, currency, payment_status} | có | |
intro | string | không |
payment
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
kind | "payment" | có | |
payment | object {id, order_id, order_code, amount, currency, checkout_url, qr_image_url, qr_payload?, code, expires_at, status} | có | |
intro | string | không |
quick_replies
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
kind | "quick_replies" | có | |
options | string[] | có |
csat
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
kind | "csat" | có | |
options | string[] | có | |
answered | boolean | không |
Kiểu dùng chung
Các đối tượng xuất hiện ở nhiều khung. Trong timeline của ready và items của resume, mỗi phần tử có thêm kind: "message" | "event" để phân biệt.
Visitor
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
id | string | có | 1–64 ký tự |
site_id | string | có | 1–64 ký tự |
channel | string | có | |
email | string | null | có | |
name | string | null | có | |
phone | string | null | có | |
avatar_url | string | null | có | |
external_id | string | null | có | |
custom_data | object | có | |
locale | "vi" | "en" | null | có | |
browser | string | null | có | |
os | string | null | có | |
device | string | null | có | |
country | string | null | có | |
city | string | null | có | |
current_url | string | null | có | |
current_title | string | null | có | |
referrer | string | null | có | |
first_seen_at | string | có | |
last_seen_at | string | có | |
identified_at | string | null | có | |
utm | object<string, string> | có | |
visits | integer | có | |
customer_id | string | null | có | |
client | "web" | "app" | có | |
app | object {id, platform, version, sdk} | null | không | |
current_screen | string | null | không |
Conversation
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
id | string | có | 1–64 ký tự |
site_id | string | có | 1–64 ký tự |
visitor_id | string | có | 1–64 ký tự |
channel | string | có | |
channel_account_id | string | null | không | |
status | "open" | "pending" | "resolved" | "closed" | có | |
assignee_kind | "user" | "bot" | null | có | |
assignee_id | string | null | có | |
seq | integer | có | |
visitor_unread | integer | có | |
agent_unread | integer | có | |
last_message_at | string | null | có | |
last_message_preview | string | null | có | |
last_message_sender_type | "visitor" | "agent" | "bot" | "system" | null | có | |
last_visitor_message_at | string | null | không | |
first_response_at | string | null | có | |
resolved_at | string | null | có | |
tags | string[] | có | |
member_ids | string[] | có | |
flag | "hidden" | "spam" | null | có | |
snoozed_until | string | không | |
muted_by | string[] | không | |
subject | string | null | có | |
created_at | string | có | |
updated_at | string | có |
Message
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
id | string | có | 1–64 ký tự |
conversation_id | string | có | 1–64 ký tự |
seq | integer | có | |
sender_type | "visitor" | "agent" | "bot" | "system" | có | |
sender_id | string | null | có | |
sender_name | string | null | không | |
visibility | "public" | "private" | có | |
content_type | "text" | "file" | có | |
content | string | có | |
attachments | Attachment[] (Attachment) | có | |
client_id | string | null | không | |
metadata | object | không | |
delivery_status | "pending" | "sent" | "failed" | null | không | |
delivery_error | string | null | không | |
read_at | string | null | không | |
created_at | string | có | |
kind | "message" | có |
Event
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
id | string | có | 1–64 ký tự |
conversation_id | string | có | 1–64 ký tự |
seq | integer | có | |
type | string | có | |
actor_type | "visitor" | "agent" | "bot" | "system" | có | |
actor_id | string | null | có | |
data | object | có | |
created_at | string | có | |
kind | "event" | có |
Attachment
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
id | string | có | 1–64 ký tự |
name | string | có | |
size | integer | có | |
type | string | có | |
url | string | không |
Availability
| Trường | Kiểu | Bắt buộc | Ràng buộc |
|---|---|---|---|
online | boolean | có | |
within_hours | boolean | có | |
next_open_at | string | null | có | |
agents_online | integer | có |