Mục này hướng dẫn bạn sử dụng Partner API để gửi tin nhắn chủ động đến khách hàng qua nhiều kênh (Messenger, Zalo OA, Zalo ZNS) — từ bất kỳ hệ thống bên ngoài nào: website, CRM, n8n, WooCommerce, hoặc app mobile.
| Method | Endpoint | Chức năng | Kênh |
|---|---|---|---|
POST | /partner/bizs/{bizAlias}/send | Gửi block/cards đến khách hàng | Messenger |
POST | /.../threads/{tid}/messages | Gửi tin nhắn vào thread cụ thể | Đa kênh |
GET | /.../threads/{tid}/messages | Lấy lịch sử tin nhắn | Đa kênh |
POST | /.../pages/{page_pid}/zaloweb-messages | Gửi tin nhắn Zalo OA Web | Zalo OA |
POST | /.../pages/{page_pid}/send-zns | Gửi ZNS notification | Zalo ZNS |
POST /partner/bizs/{bizAlias}/send
Đây là endpoint linh hoạt nhất — cho phép bạn gửi tin nhắn đến một khách hàng Messenger bằng cách chỉ định block_id (chạy kịch bản đã tạo sẵn) hoặc cards (tự tạo nội dung tin nhắn inline).
SendBlockDto| Field | Type | Bắt buộc | Mô tả |
|---|---|---|---|
customer_pid | string | ✅ | Mã khách hàng nhận tin |
page_pid | string | ✅ | Mã trang Messenger gửi tin |
block_id | string | ❌ | ID của block kịch bản đã tạo trên SmaxAI |
cards | array‹CardDto› | ❌ | Danh sách cards tự tạo nội dung inline |
attrs | array‹AttrDto› | ❌ | Cập nhật attribute KH trước khi gửi |
messenger_tag | string | ❌ | Tag cho phép gửi ngoài 24h window |
messenger_topic_id | string | ❌ | Topic ID (dùng cho Recurring Notification) |
⚠️ Lưu ý chính sách Meta: Facebook Messenger chỉ cho phép gửi tin nhắn trong vòng 24h sau lần tương tác cuối của khách. Để gửi sau 24h, bạn cần sử dụng
messenger_taghoặc tính năng Recurring Notification (messenger_topic_id).
CardDto — Mô tả một card tin nhắn:
| Field | Type | Mô tả |
|---|---|---|
block_id | string | Tham chiếu đến block khác (nếu có) |
config | object | Cấu hình nội dung card (text, image, buttons…) |
platform | string | Nền tảng mục tiêu |
type | object | Loại card (text, image, gallery, quick_reply…) |
AttrDto — Cập nhật thuộc tính KH kèm theo:
| Field | Type | Mô tả |
|---|---|---|
name | string | Tên attribute |
value | string | Giá trị mới |
Khi bạn đã xây kịch bản (script) trên giao diện SmaxAI, mỗi block có một block_id. Bạn chỉ cần truyền ID đó — không cần xây dựng nội dung phức tạp trong code.
Ví dụ: Gửi block xác nhận đơn hàng
bashcurl -X POST "https://api.smax.ai/partner/bizs/my-shop/send" \ -H "Authorization: Bearer <TOKEN>" \ -H "Content-Type: application/json" \ -d '{ "customer_pid": "cust_xyz789", "page_pid": "page_abc123", "block_id": "block_order_confirm", "attrs": [ { "name": "order_id", "value": "DH20260709001" }, { "name": "order_total", "value": "850000" } ] }'
Giải thích luồng:
Hệ thống bên ngoài SmaxAI ┌──────────────────┐ ┌──────────────────────┐ │ WooCommerce / │ POST /send │ │ │ ERP tạo đơn │─────────────►│ 1. Cập nhật attrs │ │ │ │ (order_id, │ │ payload: │ │ order_total) │ │ block_id + │ │ │ │ attrs │ │ 2. Chạy block │ │ │ │ "order_confirm" │ │ │ │ │ │ │ │ 3. Block sử dụng │ │ │ │ {{order_id}} và │ │ │ │ {{order_total}} │ │ │ │ trong tin nhắn │ │ │ │ │ │ │ │ 4. Gửi Messenger ──────► Khách hàng └──────────────────┘ └──────────────────────┘
💡 Mẹo: Trong block trên SmaxAI, bạn dùng cú pháp
{{order_id}}và{{order_total}}để chèn giá trị attribute động vào nội dung tin nhắn. Khi gọi API, chỉ cần truyền attrs tương ứng — SmaxAI sẽ tự thay thế.
Python:
pythonimport requests import os BASE_URL = "https://api.smax.ai" TOKEN = os.environ["SMAX_API_TOKEN"] BIZ_ALIAS = "my-shop" def send_block(customer_pid, page_pid, block_id, attrs=None): """Gửi tin nhắn bằng block_id đã tạo trên SmaxAI.""" url = f"{BASE_URL}/partner/bizs/{BIZ_ALIAS}/send" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json" } payload = { "customer_pid": customer_pid, "page_pid": page_pid, "block_id": block_id, "attrs": attrs or [] } response = requests.post(url, json=payload, headers=headers) response.raise_for_status() return response.json() # Gửi block xác nhận đơn hàng result = send_block( customer_pid="cust_xyz789", page_pid="page_abc123", block_id="block_order_confirm", attrs=[ {"name": "order_id", "value": "DH20260709001"}, {"name": "order_total", "value": "850,000đ"}, {"name": "delivery_date", "value": "12/07/2026"} ] ) print("Sent:", result)
Khi bạn muốn tự tạo nội dung tin nhắn từ code mà không cần block sẵn trên SmaxAI.
Ví dụ: Gửi tin nhắn text đơn giản
bashcurl -X POST "https://api.smax.ai/partner/bizs/my-shop/send" \ -H "Authorization: Bearer <TOKEN>" \ -H "Content-Type: application/json" \ -d '{ "customer_pid": "cust_xyz789", "page_pid": "page_abc123", "cards": [ { "platform": "messenger", "type": { "name": "text" }, "config": { "text": "Chào bạn! Đơn hàng DH20260709001 đã được xác nhận. Dự kiến giao hàng ngày 12/07/2026. Cảm ơn bạn!" } } ] }'
Ví dụ: Gửi ảnh + text
bashcurl -X POST "https://api.smax.ai/partner/bizs/my-shop/send" \ -H "Authorization: Bearer <TOKEN>" \ -H "Content-Type: application/json" \ -d '{ "customer_pid": "cust_xyz789", "page_pid": "page_abc123", "cards": [ { "platform": "messenger", "type": { "name": "text" }, "config": { "text": "📦 Đơn hàng của bạn đang được đóng gói!" } }, { "platform": "messenger", "type": { "name": "image" }, "config": { "url": "https://my-shop.com/images/packing-photo.jpg" } } ] }'
JavaScript:
javascriptasync function sendCards(customerPid, pagePid, cards) { const response = await fetch( `${BASE_URL}/partner/bizs/${BIZ_ALIAS}/send`, { method: "POST", headers: { "Authorization": `Bearer ${TOKEN}`, "Content-Type": "application/json" }, body: JSON.stringify({ customer_pid: customerPid, page_pid: pagePid, cards }) } ); if (!response.ok) { const error = await response.text(); throw new Error(`Send failed (${response.status}): ${error}`); } return response.json(); } // Gửi tin text + quick reply await sendCards("cust_xyz789", "page_abc123", [ { platform: "messenger", type: { name: "text" }, config: { text: "Bạn có hài lòng với đơn hàng không?" } }, { platform: "messenger", type: { name: "quick_reply" }, config: { text: "Chọn đánh giá:", options: [ { title: "⭐ Rất hài lòng", block_id: "block_review_5star" }, { title: "😐 Bình thường", block_id: "block_review_3star" }, { title: "😞 Chưa hài lòng", block_id: "block_review_1star" } ] } } ]);
| Tiêu chí | Block ID | Cards (inline) |
|---|---|---|
| Độ phức tạp code | Thấp — chỉ truyền ID | Cao — phải xây cấu trúc JSON |
| Linh hoạt nội dung | Nội dung cố định trên SmaxAI | Tạo nội dung động hoàn toàn từ code |
| Ai quản lý nội dung? | Marketing team (trên GUI) | Developer (trong code) |
| Dữ liệu động | Dùng attrs + {{variable}} | Nội suy trực tiếp trong code |
| Khi nào dùng? | Kịch bản lặp lại, chuẩn hóa | Tin nhắn 1-off, nội dung phụ thuộc logic |
💡 Khuyến nghị: Ưu tiên Block ID + attrs cho hầu hết trường hợp. Để marketing team quản lý nội dung trên GUI, developer chỉ truyền dữ liệu — phân tách rõ ràng trách nhiệm.
POST /partner/bizs/{bizAlias}/pages/{page_pid}/threads/{tid}/messages
Endpoint này gửi tin nhắn trực tiếp vào một cuộc hội thoại đã tồn tại — phù hợp khi bạn cần phản hồi trong ngữ cảnh (ví dụ: nhân viên CSKH gửi qua CRM nội bộ, hoặc bot reply trong thread).
| Parameter | Mô tả |
|---|---|
bizAlias | Mã doanh nghiệp |
page_pid | Mã trang kênh |
tid | Thread ID — mã cuộc hội thoại |
CreateMessageDto| Field | Type | Bắt buộc | Mô tả |
|---|---|---|---|
messages | array‹BodyMessageDto› | ✅ | Danh sách tin nhắn cần gửi |
type | string | ❌ | Loại tin nhắn |
reply_to_pid | string | ❌ | PID tin nhắn đang reply (trả lời tin cụ thể) |
comment_id | string | ❌ | ID comment (nếu reply từ comment) |
send_via_extention | boolean | ❌ | Gửi qua extension |
wait | boolean | ❌ | Đợi xử lý xong mới trả response |
func | object | ❌ | Hàm xử lý bổ sung |
BodyMessageDtoMỗi tin nhắn trong mảng messages có thể chứa một hoặc nhiều loại nội dung:
| Field | Type | Mô tả |
|---|---|---|
message | string | Nội dung văn bản |
picture | string | URL hình ảnh |
video | string | URL video |
audio | string | URL file âm thanh |
file | string | URL file đính kèm |
sticker | string | Sticker ID |
product_aliases | array | Danh sách alias sản phẩm đính kèm |
message_tag | string | Tag tin nhắn (Messenger 24h policy) |
temp_id | string | ID tạm (dùng cho client-side tracking) |
bashcurl -X POST "https://api.smax.ai/partner/bizs/my-shop/pages/page_abc123/threads/thread_001/messages" \ -H "Authorization: Bearer <TOKEN>" \ -H "Content-Type: application/json" \ -d '{ "messages": [ { "message": "Dạ, đây là hình ảnh sản phẩm bạn yêu cầu ạ:", "picture": "https://my-shop.com/products/sp001.jpg" } ] }'
pythondef send_thread_message(page_pid, tid, text=None, picture=None, file_url=None): """Gửi tin nhắn vào thread đã tồn tại.""" url = f"{BASE_URL}/partner/bizs/{BIZ_ALIAS}/pages/{page_pid}/threads/{tid}/messages" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json" } msg = {} if text: msg["message"] = text if picture: msg["picture"] = picture if file_url: msg["file"] = file_url payload = {"messages": [msg]} response = requests.post(url, json=payload, headers=headers) response.raise_for_status() return response.json() # Gửi bảng giá PDF vào thread send_thread_message( page_pid="page_abc123", tid="thread_001", text="Gửi bạn bảng giá tháng 7/2026 ạ. Bạn xem và phản hồi giúp mình nhé!", file_url="https://my-shop.com/files/bang-gia-t7-2026.pdf" )
pythondef reply_to_message(page_pid, tid, reply_to_pid, text): """Reply trực tiếp một tin nhắn cụ thể trong thread.""" url = f"{BASE_URL}/partner/bizs/{BIZ_ALIAS}/pages/{page_pid}/threads/{tid}/messages" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json" } payload = { "reply_to_pid": reply_to_pid, "messages": [{"message": text}] } response = requests.post(url, json=payload, headers=headers) response.raise_for_status() return response.json() # Reply tin nhắn msg_abc trong thread reply_to_message( page_pid="page_abc123", tid="thread_001", reply_to_pid="msg_abc", text="Dạ, mình đã kiểm tra lại đơn hàng của bạn rồi ạ. Hàng sẽ giao trong hôm nay!" )
GET /partner/bizs/{bizAlias}/pages/{page_pid}/threads/{tid}/messages
⚠️ Yêu cầu role: GET_LIST_MESSAGE
| Parameter | Type | Bắt buộc | Mô tả |
|---|---|---|---|
limit | number | ❌ | Số tin nhắn trả về |
sort | string | ❌ | Sắp xếp (asc / desc) |
cusor | string | ❌ | Con trỏ phân trang (cursor-based pagination) |
keyword | string | ❌ | Tìm kiếm theo từ khóa trong nội dung |
after | string | ❌ | Lấy tin nhắn sau thời điểm này |
before | string | ❌ | Lấy tin nhắn trước thời điểm này |
around_pid | string | ❌ | Lấy tin nhắn xung quanh 1 tin nhắn cụ thể |
populate | array | ❌ | Populate thêm thông tin liên quan |
📌 Lưu ý: Tên param phân trang trong API là
cusor(không phảicursor) — đây là tên gốc từ Swagger spec.
bashcurl -X GET "https://api.smax.ai/partner/bizs/my-shop/pages/page_abc123/threads/thread_001/messages?limit=20&sort=desc" \ -H "Authorization: Bearer <TOKEN>"
bashcurl -X GET "https://api.smax.ai/partner/bizs/my-shop/pages/page_abc123/threads/thread_001/messages?keyword=đơn%20hàng&limit=10" \ -H "Authorization: Bearer <TOKEN>"
pythondef get_thread_messages(page_pid, tid, limit=20, sort="desc", keyword=None, after=None, before=None): """Lấy lịch sử tin nhắn trong thread.""" url = f"{BASE_URL}/partner/bizs/{BIZ_ALIAS}/pages/{page_pid}/threads/{tid}/messages" headers = {"Authorization": f"Bearer {TOKEN}"} params = {"limit": limit, "sort": sort} if keyword: params["keyword"] = keyword if after: params["after"] = after if before: params["before"] = before response = requests.get(url, headers=headers, params=params) response.raise_for_status() return response.json() # Lấy tin nhắn trong khoảng 1-7/07/2026 messages = get_thread_messages( page_pid="page_abc123", tid="thread_001", limit=50, sort="asc", after="2026-07-01T00:00:00Z", before="2026-07-07T23:59:59Z" ) for msg in messages.get("data", []): sender = msg.get("sender", {}).get("name", "Unknown") text = msg.get("message", "") print(f"[{sender}]: {text}")
POST /partner/bizs/{bizAlias}/pages/{page_pid}/zaloweb-messages
Gửi tin nhắn qua kênh Zalo Cá nhân đã kết nối với SmaxAI.
SendMessageZalowebDto| Field | Type | Mô tả |
|---|---|---|
uid | string | User ID Zalo của người nhận |
message | string | Nội dung văn bản |
image | string | URL hình ảnh đơn |
images | array | Danh sách URL nhiều hình ảnh |
file | string | URL file đính kèm |
grid | string | Group ID (nếu gửi vào nhóm) |
groupLayoutId | number | Layout ID nhóm |
clientId | number | Client ID (tracking) |
isBroadcast | number | 1 = gửi broadcast, 0 = gửi thường |
stickerId | string | Sticker ID |
mentionInfo | string | Thông tin mention (@tag ai đó) |
bashcurl -X POST "https://api.smax.ai/partner/bizs/my-shop/pages/page_zalo_001/zaloweb-messages" \ -H "Authorization: Bearer <TOKEN>" \ -H "Content-Type: application/json" \ -d '{ "uid": "zalo_user_12345", "message": "Chào bạn! Đơn hàng DH001 của bạn đã được giao thành công. Cảm ơn bạn đã ủng hộ shop ạ! 🎉" }'
pythondef send_zalo_message(page_pid, uid, message=None, image=None, file_url=None): """Gửi tin nhắn qua kênh Zalo OA.""" url = f"{BASE_URL}/partner/bizs/{BIZ_ALIAS}/pages/{page_pid}/zaloweb-messages" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json" } payload = {"uid": uid} if message: payload["message"] = message if image: payload["image"] = image if file_url: payload["file"] = file_url response = requests.post(url, json=payload, headers=headers) response.raise_for_status() return response.json() # Gửi tin nhắn text + ảnh send_zalo_message( page_pid="page_zalo_001", uid="zalo_user_12345", message="Sản phẩm mới vừa về! Bạn xem thử nhé 👇", image="https://my-shop.com/products/new-arrival.jpg" )
POST /partner/bizs/{bizAlias}/pages/{page_pid}/send-zns
ZNS là dịch vụ gửi thông báo chủ động qua Zalo (tương tự SMS nhưng qua Zalo). Khác với tin nhắn Zalo OA thường, ZNS không yêu cầu khách đã tương tác trước — chỉ cần SĐT đã đăng ký Zalo.
SendZnsPartnerDto| Field | Type | Bắt buộc | Mô tả |
|---|---|---|---|
phone | string | ✅ | SĐT người nhận (đã đăng ký Zalo) |
template_id | string | ✅ | ID template ZNS đã được Zalo duyệt |
custom_page_pid | string | ✅ | Mã trang Zalo OA gửi |
params | array | ❌ | Danh sách tham số thay thế trong template |
attrs | array | ❌ | Cập nhật attribute KH trên SmaxAI |
1. Tạo template ZNS 2. Zalo duyệt template 3. Gọi API send-zns trên Zalo Business (1-3 ngày) với template_id ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Template: │ │ Status: │ │ POST │ │ "Xác nhận │──────────►│ APPROVED │──────────────►│ /send-zns │ │ đơn hàng │ │ │ │ │ │ {{1}}" │ │ template_id │ │ phone + │ │ │ │ = "tpl_123" │ │ template_id │ └─────────────┘ └─────────────┘ │ + params │ └──────┬──────┘ │ ▼ Khách nhận ZNS trên Zalo
bashcurl -X POST "https://api.smax.ai/partner/bizs/my-shop/pages/page_zalo_001/send-zns" \ -H "Authorization: Bearer <TOKEN>" \ -H "Content-Type: application/json" \ -d '{ "phone": "0901234567", "template_id": "tpl_order_confirm_v2", "custom_page_pid": "page_zalo_001", "params": [ { "name": "customer_name", "value": "Nguyễn Văn A" }, { "name": "order_id", "value": "DH20260709001" }, { "name": "total_amount", "value": "850,000đ" }, { "name": "delivery_date", "value": "12/07/2026" } ] }'
Python:
pythondef send_zns(phone, template_id, page_pid, params=None, attrs=None): """Gửi ZNS notification qua Zalo.""" url = f"{BASE_URL}/partner/bizs/{BIZ_ALIAS}/pages/{page_pid}/send-zns" headers = { "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json" } payload = { "phone": phone, "template_id": template_id, "custom_page_pid": page_pid, "params": params or [], "attrs": attrs or [] } response = requests.post(url, json=payload, headers=headers) response.raise_for_status() return response.json() # Gửi ZNS xác nhận đơn hàng send_zns( phone="0901234567", template_id="tpl_order_confirm_v2", page_pid="page_zalo_001", params=[ {"name": "customer_name", "value": "Nguyễn Văn A"}, {"name": "order_id", "value": "DH20260709001"}, {"name": "total_amount", "value": "850,000đ"}, {"name": "delivery_date", "value": "12/07/2026"} ], attrs=[ {"name": "last_zns_sent", "value": "2026-07-09T10:30:00Z"} ] )
💡 Mẹo: Trường
attrstrong ZNS cho phép bạn vừa gửi ZNS, vừa cập nhật attribute KH trên SmaxAI cùng lúc — tiện lợi để tracking lần gửi ZNS cuối.
Kịch bản: Khi WooCommerce có đơn hàng mới, hệ thống tự động gửi xác nhận qua cả Messenger lẫn Zalo ZNS, đảm bảo khách nhận được thông báo dù đang online ở kênh nào.
WooCommerce n8n Workflow SmaxAI ┌──────────┐ ┌──────────────────────────┐ ┌──────────────┐ │ Đơn hàng │ │ 1. Webhook: New Order │ │ │ │ mới │────►│ │ │ │ │ │ │ 2. Extract data: │ │ │ │ • KH │ │ name, phone, order_id, │ │ │ │ • SĐT │ │ total, items │ │ │ │ • SP │ │ │ │ │ │ • Tổng │ │ 3. Branch: │ │ │ │ │ │ │ │ │ │ │ │ ┌─ Messenger ────────────────► POST /send │ │ │ │ │ (block_id + attrs) │ │ → Block xác │ │ │ │ │ │ │ nhận ĐH │ │ │ │ │ │ │ │ │ │ │ └─ Zalo ZNS ────────────────► POST /send-zns │ │ │ │ (template + params) │ │ → ZNS thông │ │ │ │ │ │ báo ĐH │ │ │ │ 4. Log result │ │ │ └──────────┘ └──────────────────────────┘ └──────────────┘
| Field | Giá trị |
|---|---|
| Method | POST |
| Path | /woocommerce-order |
| Authentication | Header Auth (optional) |
javascript// Trích xuất từ payload WooCommerce const order = $input.item.json; return { order_id: `DH${order.id}`, customer_name: `${order.billing.first_name} ${order.billing.last_name}`, phone: order.billing.phone.replace("+84", "0"), total: new Intl.NumberFormat("vi-VN").format(order.total) + "đ", items_summary: order.line_items.map(i => i.name).join(", "), customer_pid: order.meta_data?.find(m => m.key === "smax_customer_pid")?.value, page_pid_messenger: "page_fb_001", page_pid_zalo: "page_zalo_001" };
| Field | Giá trị |
|---|---|
| Method | POST |
| URL | https://api.smax.ai/partner/bizs/{{ $env.BIZ_ALIAS }}/send |
Body:
json{ "customer_pid": "{{ $json.customer_pid }}", "page_pid": "{{ $json.page_pid_messenger }}", "block_id": "block_order_confirm", "attrs": [ { "name": "order_id", "value": "{{ $json.order_id }}" }, { "name": "order_total", "value": "{{ $json.total }}" }, { "name": "items", "value": "{{ $json.items_summary }}" } ] }
| Field | Giá trị |
|---|---|
| Method | POST |
| URL | https://api.smax.ai/partner/bizs/{{ $env.BIZ_ALIAS }}/pages/{{ $json.page_pid_zalo }}/send-zns |
Body:
json{ "phone": "{{ $json.phone }}", "template_id": "tpl_order_confirm_v2", "custom_page_pid": "{{ $json.page_pid_zalo }}", "params": [ { "name": "customer_name", "value": "{{ $json.customer_name }}" }, { "name": "order_id", "value": "{{ $json.order_id }}" }, { "name": "total_amount", "value": "{{ $json.total }}" } ] }
javascript// Kiểm tra kết quả 2 nhánh const messengerOk = $("Node 3A").first().json.statusCode < 300; const zaloOk = $("Node 3B").first().json.statusCode < 300; return { order_id: $json.order_id, messenger_sent: messengerOk, zalo_sent: zaloOk, all_sent: messengerOk && zaloOk };
Kịch bản: Sau khi giao hàng thành công, hệ thống tự động gửi chuỗi tin nhắn follow-up theo lịch.
pythonimport time from datetime import datetime, timedelta def send_followup_sequence(customer_pid, page_pid, order_id): """Gửi chuỗi follow-up sau khi giao hàng thành công.""" sequence = [ { "delay_hours": 2, "block_id": "block_delivery_confirm", "attrs": [{"name": "order_id", "value": order_id}], "description": "Xác nhận đã nhận hàng" }, { "delay_hours": 24, "block_id": "block_review_request", "attrs": [{"name": "order_id", "value": order_id}], "description": "Yêu cầu đánh giá" }, { "delay_hours": 72, "block_id": "block_cross_sell", "attrs": [], "description": "Gợi ý sản phẩm liên quan" } ] for step in sequence: print(f"⏳ Chờ {step['delay_hours']}h trước khi gửi: {step['description']}") time.sleep(step["delay_hours"] * 3600) # Production: dùng task queue try: result = send_block( customer_pid=customer_pid, page_pid=page_pid, block_id=step["block_id"], attrs=step["attrs"] ) print(f"✅ Đã gửi: {step['description']}") except Exception as e: print(f"❌ Lỗi gửi {step['description']}: {e}") break # Dừng sequence nếu gặp lỗi # Sử dụng send_followup_sequence( customer_pid="cust_xyz789", page_pid="page_abc123", order_id="DH20260709001" )
⚠️ Quan trọng: Trong production, không dùng
**time.sleep()**— hãy dùng task queue (Celery, Bull) hoặc n8n Wait node để lập lịch. Ví dụ trên chỉ minh họa logic sequence.
Webhook Wait 2h Send Block Wait 24h Send Block (Order ─────► (Chờ 2h) ────► "Đã nhận ─────► (Chờ 24h) ───► "Xin đánh delivered) hàng chưa?" giá" │ ▼ (Nếu KH trả lời "Chưa") Notify admin
| Lỗi | Nguyên nhân | Giải pháp |
|---|---|---|
400 — Invalid customer_pid | customer_pid không tồn tại | Kiểm tra KH tồn tại trước khi gửi |
400 — Invalid block_id | block_id không tồn tại trong Business | Kiểm tra block trên GUI SmaxAI |
400 — messages required | Body thiếu trường messages | Đảm bảo body có mảng messages |
401 — Unauthorized | Token hết hạn hoặc không hợp lệ | Refresh token |
403 — 24h window expired | Gửi Messenger sau 24h không tương tác | Dùng messenger_tag hoặc ZNS |
429 — Rate limited | Gửi quá nhanh | Retry sau 1-5 giây |
ZNS — Template not found | template_id không tồn tại hoặc chưa được duyệt | Kiểm tra trên Zalo Business |
ZNS — Invalid phone | SĐT chưa đăng ký Zalo | Fallback sang SMS hoặc Messenger |
pythonimport time def send_with_retry(send_func, max_retries=3, base_delay=1): """Gửi tin nhắn với retry logic.""" for attempt in range(max_retries): try: return send_func() except requests.exceptions.HTTPError as e: status = e.response.status_code if status == 429: # Rate limited — exponential backoff delay = base_delay * (2 ** attempt) print(f"⏳ Rate limited. Retry sau {delay}s...") time.sleep(delay) continue elif status >= 500: # Server error — retry delay = base_delay * (2 ** attempt) print(f"⚠️ Server error {status}. Retry sau {delay}s...") time.sleep(delay) continue else: # Client error (400, 401, 403) — không retry print(f"❌ Client error {status}: {e.response.text}") raise raise Exception(f"Gửi thất bại sau {max_retries} lần retry") # Sử dụng result = send_with_retry( lambda: send_block("cust_xyz789", "page_abc123", "block_order_confirm") )
pythonimport time def bulk_send_block(customers, page_pid, block_id, delay_ms=200): """Gửi tin nhắn hàng loạt cho danh sách KH, giãn cách để tránh rate limit.""" results = {"success": 0, "failed": 0, "errors": []} for i, customer in enumerate(customers): try: send_block( customer_pid=customer["pid"], page_pid=page_pid, block_id=block_id, attrs=customer.get("attrs", []) ) results["success"] += 1 print(f"✅ [{i+1}/{len(customers)}] Sent to {customer['pid']}") except Exception as e: results["failed"] += 1 results["errors"].append({"pid": customer["pid"], "error": str(e)}) print(f"❌ [{i+1}/{len(customers)}] Failed: {e}") # Giãn cách giữa các request time.sleep(delay_ms / 1000) return results # Gửi cho 100 khách hàng, mỗi request cách nhau 200ms customers = [ {"pid": "cust_001", "attrs": [{"name": "promo", "value": "SALE50"}]}, {"pid": "cust_002", "attrs": [{"name": "promo", "value": "SALE50"}]}, # ... thêm KH ] report = bulk_send_block(customers, "page_abc123", "block_promo_july", delay_ms=200) print(f" Kết quả: {report['success']} thành công, {report['failed']} thất bại")
| Tiêu chí | POST /send (Block) | POST /messages (Thread) | Zalo Web | Zalo ZNS |
|---|---|---|---|---|
| Kênh | Messenger | Đa kênh | Zalo OA | Zalo |
| Yêu cầu KH tương tác trước? | Có (24h window) | Có | Có | Không — chỉ cần SĐT |
| Cần gì? | customer_pid + page_pid | page_pid + tid | uid (Zalo user) | phone + template_id |
| Nội dung | Block hoặc Cards | Text/Image/File/Video | Text/Image/File | Template có tham số |
| Chi phí | Miễn phí | Miễn phí | Miễn phí | Có phí (theo ZNS pricing) |
| Use case chính | Gửi kịch bản bot | Trả lời trong hội thoại | Chăm sóc KH Zalo | Thông báo giao dịch |
| Giới hạn nội dung | Linh hoạt | Linh hoạt | Linh hoạt | Cố định theo template |
Trên trang này