Action Node - Đa kênh: Zalo cá nhân (Omnichannel - Zalo Personal)

I. Tổng quan

Tài liệu đặc tả nghiệp vụ và kỹ thuật cho Action Node “Đa kênh: Zalo cá nhân” trong AdaFlow. Node cung cấp một hành động cấp cao tích hợp: Kết bạn, gửi tin nhắn & liên kết khách hàng qua Zalo cá nhân, cho phép linh hoạt cấu hình một hoặc nhiều thao tác liên hoàn trong cùng một node.

Thiết kế Figma tham chiếu: Figma - Node Đa kênh Zalo cá nhân

1. Vấn đề

  • Trước đây, quy trình chăm sóc khách hàng qua Zalo cá nhân trên luồng automation bị phân mảnh thành nhiều node rời rạc hoặc trải dài qua 6 tab cấu hình phức tạp với tab Auth riêng biệt.
  • Giao diện cũ bắt buộc người dùng hiểu các khái niệm kỹ thuật sâu như “Nguồn hội thoại”, “Nguồn liên hệ”, gây nhầm lẫn giữa Contact ID và Số điện thoại (SĐT), dẫn đến tỷ lệ cấu hình sai và lỗi runtime cao.
  • Khi cần thực hiện chuỗi hành động phổ biến: (1) Kết bạn $\rightarrow$ (2) Gửi tin nhắn chào mừng $\rightarrow$ (3) Gửi tin vào hội thoại $\rightarrow$ (4) Gắn khách hàng vào Contact CRM, người thiết lập flow phải ghép nối 3–4 node độc lập, khó kiểm soát thứ tự thực thi và khó xử lý lỗi khi một bước trung gian bị gián đoạn.

2. Giải pháp

  • Tích hợp toàn bộ nghiệp vụ tương tác Zalo cá nhân vào một Action Node duy nhất: Đa kênh: Zalo cá nhân (Hành động: Kết bạn, gửi tin nhắn & liên kết khách hàng qua Zalo cá nhân).
  • Tinh gọn panel cấu hình thành 3 tab chuẩn: Hành động, Cài đặt, Đầu ra (loại bỏ hoàn toàn tab Auth và nút Lưu riêng lẻ; sử dụng cơ chế lưu tự động của canvas).
  • Hỗ trợ 4 thao tác độc lập dạng Checkbox trong cùng một node:
    1. Gửi lời mời kết bạn
    2. Gửi tin nhắn Zalo đầu tiên
    3. Gửi tin vào hội thoại
    4. Liên kết liên hệ
  • Phân định rõ ràng 2 cơ chế xác định đối tượng nhận tin:
    • Người nhận (recipient_phone): Dành cho trường hợp tiếp cận mới chưa có hội thoại (nhập SĐT trực tiếp hoặc map biến SĐT).
    • Thread ID (thread_id): Dành cho trường hợp đã có hội thoại (Combobox cho phép chọn trực tiếp thread chat, hoặc nhập SĐT để tự động tra cứu thread thuộc tài khoản Zalo đã chọn, hoặc map biến).
  • Luồng thực thi cố định, có cơ chế bảo toàn kết quả thao tác trước và chống gửi lặp (idempotency).

3. Đối tượng

  • Automation Ops / Marketing Ops / Sales Ops: Người cấu hình các kịch bản tự động hóa chăm sóc khách hàng đa kênh.
  • Hệ thống AdaFlow Engine: Trình thực thi node, tự động resolve tài khoản, tìm kiếm thread và gọi API Zalo.
  • Khách hàng cuối: Nhận lời mời kết bạn và tin nhắn chăm sóc cá nhân hóa từ đúng nhân viên phụ trách.

4. Tầm nhìn / Insight

  • Mở rộng hỗ trợ gửi đính kèm đa phương tiện (hình ảnh, tài liệu PDF báo giá, hợp đồng).
  • Tích hợp AI Agent phản hồi tự động khi khách hàng nhắn tin lại vào thread vừa tạo.

II. Yêu cầu chức năng

1. Danh sách tính năng

  • Loại node: Action Node
  • Nhóm node: Đa kênh (Omnichannel)
  • Hành động cấp cao: Kết bạn, gửi tin nhắn & liên kết khách hàng qua Zalo cá nhân
  • Danh sách 4 thao tác tùy chọn (Checkbox):
    1. Gửi lời mời kết bạn: Gửi lời chào kết bạn kèm lời giới thiệu tùy biến.
    2. Gửi tin nhắn Zalo đầu tiên: Gửi tin nhắn mở đầu (hỗ trợ Tin nhắn nhanh và chèn biến).
    3. Gửi tin vào hội thoại: Gửi tin nhắn tiếp theo vào thread cá nhân hoặc thread nhóm đã có.
    4. Liên kết liên hệ: Tự động gắn kết thread chat với đối tượng Contact trong CRM.

2. Đặc tả chi tiết

2.1. Tổ chức 3 Tab cấu hình

┌────────────────────────────────────────────────────────┐
│  Hành động  │  [Cài đặt]  │  Đầu ra                   │
├────────────────────────────────────────────────────────┤
│ [Tài khoản Zalo *] [Chọn tài khoản / Biến User ▾]     │
│                                                        │
│ [Cách xác định đối tượng: Điền 1 trong 2 ô dưới đây]    │
│  • Người nhận: [Nhập SĐT hoặc ]          │
│  • Thread ID:  [Chọn thread hoặc nhập SĐT để tìm... ▾] │
│                                                        │
│ ☑ Gửi lời mời kết bạn                                  │
│   Lời giới thiệu: [Nhập nội dung...]        [(+) Biến] │
│                                                        │
│ ☑ Gửi tin nhắn Zalo đầu tiên                           │
│   [Tin nhắn nhanh ▾]                        [(+) Biến] │
│   Nội dung tin nhắn: [Nhập nội dung...]                │
│                                                        │
│ ☑ Gửi tin vào hội thoại                                │
│   Nội dung tin nhắn: [Nhập nội dung...]     [(+) Biến] │
│                                                        │
│ ☑ Liên kết liên hệ                                     │
│   Liên hệ cần liên kết: [Chọn Contact / ] │
└────────────────────────────────────────────────────────┘
A. Tab 1: Hành động
  • Hiển thị danh sách hành động của node. Phiên bản này hỗ trợ duy nhất một hành động: “Kết bạn, gửi tin nhắn & liên kết khách hàng qua Zalo cá nhân”.
  • Khi được chọn, tự động mở sang tab Cài đặt.
B. Tab 2: Cài đặt
B.1. Các trường cấu hình chung:

| Tên trường | Kiểu dữ liệu / UI | Bắt buộc | Mô tả & Ràng buộc | | :— | :— | :—: | :— | | Tài khoản Zalo | Select dropdown / Variable picker | ✅ | • Cho phép chọn 1 tài khoản Zalo cá nhân cụ thể hoặc chọn biến User ID từ bước trước ().<br>• Hiển thị: Avatar + Tên Zalo nếu chọn cứng; hiển thị chip biến nếu chọn biến.<br>• Khi đổi tài khoản: Tự động hủy lựa chọn thread cố định đã chọn của tài khoản cũ. | | **Người nhận** | Input text / Variable picker | ⚠️ *(1 trong 2)* | • Nhập SĐT trực tiếp (ví dụ: `0901234567`) hoặc chọn biến SĐT (ví dụ:).
• Không suy luận ô này là Contact ID.
• Dành cho trường hợp chưa có thread, cần kết bạn hoặc gửi tin nhắn đầu tiên. | | Thread ID (thread_id) | Combobox tìm kiếm / Variable picker | ⚠️ (1 trong 2) | • Hỗ trợ 3 cách nhập:
  1. Chọn từ danh sách: Tìm và chọn trực tiếp thread chat có sẵn trong tài khoản Zalo.
  2. Nhập SĐT: Tự động tra cứu và resolve thread cá nhân tương ứng.
  3. Chọn biến: Map biến thread_id hoặc phone từ node trước.
• Placeholder: “Chọn thread chat hoặc nhập số điện thoại”. | | Danh sách thao tác | 4 Checkbox độc lập | ✅ | Bắt buộc tick ít nhất 1 thao tác. Tick thao tác nào thì mở rộng panel cấu hình của thao tác đó. |

Ràng buộc đầu vào giữa Người nhận và Thread ID:

  • Nếu cả hai ô đều trống: Hệ thống báo lỗi validation: TARGET_REQUIRED (“Điền Người nhận hoặc Thread ID”).
  • Nếu cả hai ô đều có dữ liệu: Hệ thống báo lỗi validation: TARGET_INPUT_CONFLICT (“Chỉ điền một trong hai trường Người nhận hoặc Thread ID”). Tuyệt đối không ưu tiên ngầm một ô.
B.2. Chi tiết 4 Thao tác Checkbox:
  1. Thao tác 1: Gửi lời mời kết bạn
    • Hiển thị: Textarea “Lời giới thiệu kết bạn”, nút chèn biến (+).
    • Nội dung: Không bắt buộc (nếu để trống, Zalo gửi lời mời mặc định). Giới hạn tối đa 150 ký tự theo quy định Zalo.
    • Logic resolve đối tượng: Lấy từ ô Người nhận hoặc trích xuất danh tính từ thread cá nhân ở ô Thread ID.
    • Quy tắc: Nếu người nhận đã là bạn hoặc đang có lời mời chờ xác nhận $\rightarrow$ Bỏ qua (skipped với lý do ALREADY_FRIEND hoặc FRIEND_REQUEST_PENDING), không coi là lỗi làm dừng node.
  2. Thao tác 2: Gửi tin nhắn Zalo đầu tiên
    • Hiển thị: Dropdown chọn mẫu “Tin nhắn nhanh”, Textarea nội dung tin nhắn, nút chèn biến (+).
    • Nội dung: Bắt buộc khi tick. Giới hạn tối đa 2.000 ký tự.
    • Quy tắc chống spam: Đề xuất cơ chế chống gửi lời chào lặp theo cặp (zalo_account_id, recipient_phone) trên phạm vi workspace. Nếu đã gửi thành công trước đó, trả về skipped (FIRST_MESSAGE_ALREADY_SENT), không gửi lại khi retry.
    • Đồng bộ sau khi gửi: Hệ thống ghi nhận kết quả gửi và kích hoạt tiến trình resolve thread nội bộ để các thao tác phía sau sử dụng.
  3. Thao tác 3: Gửi tin vào hội thoại
    • Hiển thị: Textarea nội dung tin nhắn, nút chèn biến (+).
    • Nội dung: Bắt buộc khi tick. Giới hạn tối đa 2.000 ký tự.
    • Phạm vi hỗ trợ: Áp dụng cho cả thread cá nhân và thread nhóm. Với nhóm, tài khoản Zalo gửi phải còn quyền gửi tin trong nhóm.
    • Quy tắc: Bắt buộc phải có thread hợp lệ (từ ô Thread ID hoặc được resolve thành công sau khi gửi tin đầu tiên). Nếu không có thread $\rightarrow$ Báo lỗi THREAD_NOT_FOUND, không tự chuyển đổi sang thao tác Gửi tin đầu tiên.
    • Gửi nhiều lần: Cho phép gửi nhiều lần trong các phiên thực thi khác nhau của flow; chỉ áp dụng cơ chế idempotent chống gửi trùng khi retry cùng một execution ID.
  4. Thao tác 4: Liên kết liên hệ
    • Hiển thị: Ô chọn/nhập “Liên hệ cần liên kết”, cho phép tìm chọn Contact CRM hoặc map biến contact_id. Bắt buộc khi tick.
    • Phạm vi áp dụng: Dùng thread đã xác định từ cấu hình chung.
    • Quy tắc xác minh danh tính:
      • Với thread cá nhân: Bắt buộc danh tính người tham gia thread phải khớp với thông tin định danh (SĐT/Zalo UID) của Contact trước khi liên kết.
      • Với thread nhóm: Chỉ cho phép liên kết khi Contact được xác minh là một thành viên có trong nhóm đó. Tuyệt đối không lấy SĐT ngẫu nhiên xuất hiện trong tin nhắn chat làm bằng chứng liên kết.
    • Xử lý trạng thái liên kết:
      • Chưa gắn contact $\rightarrow$ Tạo liên kết mới (success).
      • Đã gắn đúng contact này từ trước $\rightarrow$ Bỏ qua hợp lệ (skipped, lý do ALREADY_LINKED).
      • Đang gắn với một contact khác $\rightarrow$ Báo lỗi xung đột CONTACT_LINK_CONFLICT, tuyệt đối không tự ý ghi đè thay thế.

2.2. Quy tắc tra cứu Thread từ Số điện thoại (Phone to Thread Lookup)

Khi người dùng nhập Số điện thoại vào ô Thread ID:

  1. Resolve tài khoản Zalo trước: Bắt buộc xác định được tài khoản Zalo cụ thể trước khi tra cứu.
  2. Chuẩn hóa SĐT: Tự động chuẩn hóa về định dạng quốc tế chuẩn của hệ thống (ví dụ: 0901234567 $\rightarrow$ +84901234567).
  3. Phạm vi tra cứu: Chỉ tìm kiếm trong các hội thoại thuộc quyền sở hữu của đúng tài khoản Zalo đã chọn, trong cùng workspace.
  4. Cơ chế đối chiếu: Chỉ đối chiếu SĐT với hồ sơ người tham gia đã xác minh hoặc bảng mapping SĐT ↔ Zalo UID. Tuyệt đối không tìm kiếm theo chuỗi text SĐT xuất hiện trong nội dung chat, tên nhóm hay ghi chú.
  5. Giới hạn kết quả: Tìm kiếm bằng SĐT chỉ resolve thread cá nhân (1-1). Với thread nhóm, bắt buộc chọn trực tiếp hoặc truyền biến thread_id.
Kết quả tra cứu Hành vi của hệ thống
Tìm thấy đúng 1 thread cá nhân Tự động gắn và sử dụng thread_id nội bộ đó.
Không tìm thấy thread nào Báo lỗi THREAD_NOT_FOUND (“Không tìm thấy hội thoại phù hợp”). Không tự tạo thread. Nếu muốn gửi mới, hướng dẫn dùng ô Người nhận.
Tìm thấy nhiều hơn 1 thread • Trên UI: Hiển thị danh sách kết quả để người dùng chọn 1 thread cụ thể.
• Runtime: Trả lỗi THREAD_AMBIGUOUS, không tự ý lấy kết quả đầu tiên.
SĐT không hợp lệ Báo lỗi INVALID_PHONE.

2.3. Thứ tự và Luồng thực thi (Execution Pipeline)

Các thao tác được tick sẽ được thực thi theo thứ tự cố định bất biến:

graph TD
    START([Bắt đầu thực thi Node]) --> VAL{Kiểm tra Tài khoản Zalo & Target}
    VAL -- Lỗi đầu vào --> ERR1[Dừng: Trả mã lỗi validate]
    VAL -- Hợp lệ --> OP1{Tick: Gửi lời mời kết bạn?}
    
    OP1 -- Có --> DO1[1. Gửi lời mời kết bạn]
    DO1 -- Thất bại --> STOP1[Dừng các bước sau: Trả lỗi]
    DO1 -- Thành công / Bỏ qua --> OP2
    OP1 -- Không --> OP2{Tick: Gửi tin đầu tiên?}
    
    OP2 -- Có --> DO2[2. Gửi tin nhắn Zalo đầu tiên]
    DO2 -- Thất bại --> STOP2[Dừng các bước sau: Trả lỗi]
    DO2 -- Thành công / Bỏ qua --> SYNC[Đồng bộ / Resolve Thread ID]
    SYNC --> OP3
    OP2 -- Không --> OP3{Tick: Gửi tin vào hội thoại?}
    
    OP3 -- Có --> DO3[3. Gửi tin vào hội thoại]
    DO3 -- Thất bại --> STOP3[Dừng các bước sau: Trả lỗi]
    DO3 -- Thành công / Bỏ qua --> OP4
    OP3 -- Không --> OP4{Tick: Liên kết liên hệ?}
    
    OP4 -- Có --> DO4[4. Liên kết liên hệ vào CRM]
    DO4 -- Thất bại --> STOP4[Dừng: Trả lỗi]
    DO4 -- Thành công / Bỏ qua --> END_NODE
    OP4 -- Không --> END_NODE([Kết thúc Node: Trả Output])
Nguyên tắc quản lý thực thi:
  1. Dừng chuỗi khi gặp lỗi (Fail-fast with State Preservation): Nếu một thao tác trong chuỗi thất bại, toàn bộ các thao tác phía sau sẽ bị hủy (not_executed). Tuy nhiên, kết quả của các thao tác đã thành công trước đó vẫn được bảo toàn nguyên vẹn (trạng thái tổng thể là partial_success).
  2. Không thực hiện lặp khi Retry: Nếu execution trước bị timeout hoặc lỗi ở bước 4, khi flow retry lại, hệ thống sẽ kiểm tra trạng thái và bỏ qua (skipped) các bước 1, 2, 3 đã thành công, chỉ thực thi bước 4.
  3. Timeout / Kết quả chưa rõ (unknown): Nếu gọi API Zalo mà bị timeout mạng chưa rõ kết quả, trả trạng thái unknown. Hệ thống đối soát ghi nhận log và không tự động gửi lại ngay để tránh spam khách hàng.

3. Danh sách nghiệp vụ & Quyết định kỹ thuật

3.1. Các quyết định nghiệp vụ đã thống nhất

  1. User liên kết nhiều tài khoản Zalo: Khi map biến User ID, hệ thống tự động lấy tài khoản Zalo được đánh dấu là Mặc định cho Automation. Nếu chưa cấu hình tài khoản mặc định, trả lỗi ACCOUNT_NOT_RESOLVED.
  2. Thread ID nhập SĐT nhưng chưa có thread: Trả lỗi THREAD_NOT_FOUND. Không tự động chuyển thành gửi tin đầu tiên (vì người dùng đang chọn đường Thread ID).
  3. Nhiều thread cá nhân trùng SĐT: Yêu cầu người dùng chọn rõ ràng; runtime trả lỗi THREAD_AMBIGUOUS, không tự động lấy thread mới nhất.
  4. Định nghĩa tin nhắn đầu tiên: Được tính là 1 lần gửi lời chào automation thành công theo cặp (zalo_account_id, recipient_phone) trong workspace.
  5. Chưa là bạn nhưng tick gửi tin đầu tiên: Gửi theo khả năng cho phép của Zalo API cá nhân. Nếu Zalo từ chối người lạ, trả lỗi failed, không tự động chờ khách đồng ý kết bạn.
  6. Thread nhóm: Bắt buộc truyền thread_id (chọn trực tiếp hoặc qua biến). Không hỗ trợ thao tác Kết bạn và Gửi tin đầu tiên trên thread nhóm (PERSONAL_THREAD_REQUIRED). Liên kết liên hệ trên nhóm bắt buộc xác minh Contact là thành viên nhóm.
  7. Thread chưa đồng bộ sau tin đầu tiên: Sau khi gửi tin đầu tiên thành công, hệ thống thử resolve thread nội bộ tối đa 3 lần theo cơ chế Backoff (giãn cách 1-2 giây). Nếu vẫn chưa đồng bộ kịp, ghi nhận tin đã gửi thành công nhưng tạm dừng các thao tác sau cần thread với mã THREAD_NOT_SYNCED.

3.2. Bảng mã lỗi và thông báo chuẩn hóa

Mã lỗi (error_code) Thông báo hiển thị (error_message) Hướng xử lý
TARGET_REQUIRED Vui lòng điền Người nhận hoặc Thread ID. Cấu hình bổ sung 1 trong 2 trường.
TARGET_INPUT_CONFLICT Chỉ được điền một trong hai trường: Người nhận hoặc Thread ID. Xóa bớt 1 trường cấu hình.
INVALID_PHONE Số điện thoại không đúng định dạng. Kiểm tra lại SĐT hoặc biến SĐT.
ACCOUNT_NOT_RESOLVED Không xác định được tài khoản Zalo từ người dùng đã chọn. Cài đặt tài khoản Zalo mặc định cho User.
ACCOUNT_UNAVAILABLE Tài khoản Zalo đang mất kết nối hoặc bị khóa. Đăng nhập lại tài khoản Zalo trên hệ thống.
THREAD_NOT_FOUND Không tìm thấy hội thoại phù hợp trong tài khoản Zalo này. Kiểm tra lại SĐT hoặc chọn đường Người nhận.
THREAD_AMBIGUOUS Tìm thấy nhiều hội thoại trùng khớp. Hãy chọn thread cụ thể. Chọn trực tiếp thread từ danh sách.
ACCOUNT_THREAD_MISMATCH Hội thoại không thuộc quyền sở hữu của tài khoản Zalo đã chọn. Kiểm tra lại tài khoản gửi tin.
THREAD_ACCESS_DENIED Không có quyền truy cập hoặc gửi tin vào hội thoại này. Kiểm tra phân quyền thành viên trong nhóm.
PERSONAL_THREAD_REQUIRED Kết bạn và gửi tin nhắn đầu tiên chỉ áp dụng cho người nhận cá nhân. Bỏ chọn kết bạn/tin đầu tiên khi chat nhóm.
THREAD_NOT_SYNCED Tin đã gửi nhưng hội thoại chưa kịp đồng bộ; chưa thể thực hiện thao tác tiếp theo. Kiểm tra lại tiến trình đồng bộ hội thoại.
CONTACT_REQUIRED Vui lòng chọn liên hệ cần liên kết. Chọn Contact ID hợp lệ.
CONTACT_NOT_FOUND Không tìm thấy thông tin liên hệ trong CRM. Kiểm tra lại record Contact.
IDENTITY_NOT_VERIFIED Chưa đủ thông tin để xác minh liên hệ thuộc về hội thoại này. Kiểm tra SĐT hoặc thành viên nhóm.
CONTACT_LINK_CONFLICT Hội thoại này đã được liên kết với một liên hệ khác trong CRM. Gỡ liên kết cũ trước khi gắn liên kết mới.
SEND_OUTCOME_UNKNOWN Chưa xác định được kết quả gửi tin từ Zalo; cần đối soát lại. Kiểm tra trạng thái mạng / webhook Zalo.

4. Giao diện (UI Specification)

  • Panel bên phải (Right Config Panel): Rộng 480px, chuẩn design system Ant Design 5.x.
  • Card Node trên Canvas:
    • Header: Icon Zalo màu xanh dương chuẩn, Title “Đa kênh”, Subtitle “Zalo cá nhân”.
    • Body: Tóm tắt các thao tác đã chọn (Tags: Kết bạn, Tin đầu, Tin nhóm, Liên kết).
    • Status indicator: Chấm xanh lá khi cấu hình hợp lệ, Chấm đỏ khi thiếu trường bắt buộc.
  • Biểu mẫu Cài đặt:
    • Textarea có thanh đếm số ký tự ở góc phải dưới (ví dụ: 45/150 ký tự cho lời mời kết bạn; 120/2000 ký tự cho tin nhắn).
    • Nút chèn biến (+) mở Variable Picker popover lọc theo node trước đó trong flow.

III. Yêu cầu phi chức năng

  • Hiệu năng & Độ trễ (Execution Latency): Thời gian thực thi toàn bộ chuỗi 4 thao tác không vượt quá 5 giây trong điều kiện mạng ổn định.
  • Giới hạn tần suất (Rate Limiting): Tự động điều tiết (queue throttling) để không gửi quá 1 tin/giây trên cùng một tài khoản Zalo cá nhân, tuân thủ chính sách chống spam của Zalo.
  • Khóa đồng thời (Distributed Concurrency Lock): Sử dụng Redis Lock theo khóa lock:zalo_send:{zalo_account_id}:{recipient_phone} trong thời gian thực thi node để chống duplicate message khi flow bị kích hoạt song song.
  • Bảo mật & Mã hóa: Toàn bộ token phiên làm việc của Zalo cá nhân và nội dung tin nhắn được mã hóa at-rest (AES-256).

IV. Dependency (Liên quan & Phụ thuộc)

  • Module Omnichannel Chat (Zalo Sync Engine): Xử lý đồng bộ tin nhắn, trạng thái kết bạn và quản lý danh sách thread_id.
  • Module CRM Contacts (dev/crm_objects/): Lưu trữ thông tin khách hàng, trường SĐT, thực hiện liên kết 1-1 giữa Contact và Thread.
  • AdaFlow Engine Core (dev/adaflow/): Quản lý trạng thái flow, điều phối context data giữa các bước, xử lý retry và logging run report.

V. API Contract (Đặc tả kỹ thuật)

1. Schema Cấu hình Node (node_config)

{
  "node_type": "action",
  "category": "omnichannel",
  "action_name": "zalo_personal_interaction",
  "config": {
    "account": {
      "type": "fixed", // "fixed" | "variable"
      "value": "zalo_acc_098765" // hoặc ""
    },
    "target": {
      "mode": "recipient_phone", // "recipient_phone" | "thread_id"
      "value": ""
    },
    "actions": {
      "send_friend_request": {
        "enabled": true,
        "message": "Xin chào , mình là chuyên viên tư vấn UCALL."
      },
      "send_first_message": {
        "enabled": true,
        "template_id": "quick_msg_01",
        "message": "Chào bạn, mình gửi thông tin giải pháp bạn vừa đăng ký nhé!"
      },
      "send_thread_message": {
        "enabled": false,
        "message": ""
      },
      "link_contact": {
        "enabled": true,
        "contact_id": ""
      }
    }
  }
}

2. Schema Dữ liệu Đầu ra (output_contract)

Tab Đầu ra của node xuất ra các biến chuẩn hóa:

Tên biến Kiểu dữ liệu Ý nghĩa
status String Trạng thái tổng thể: success, partial_success, failed, skipped, unknown
zalo_account_id String ID tài khoản Zalo thực tế đã thực thi
recipient_id String / Null Zalo UID của người nhận cá nhân (nếu có)
thread_id String / Null ID nội bộ của hội thoại chat được chọn hoặc tìm thấy
contact_id String / Null ID liên hệ CRM được sử dụng cho thao tác liên kết
friend_request.status String success, skipped, failed, not_executed, unknown
friend_request.id String / Null Mã định danh lời mời kết bạn từ Zalo
first_message.status String Kết quả gửi tin đầu tiên (success, skipped, failed, not_executed)
first_message.id String / Null ID tin nhắn đầu tiên đã gửi
message.status String Kết quả gửi tin vào hội thoại (success, skipped, failed, not_executed)
message.id String / Null ID tin nhắn gửi vào hội thoại
contact_link.status String Kết quả liên kết liên hệ (success, skipped, failed, not_executed)
error_code String / Null Mã lỗi chuẩn hóa nếu node gặp sự cố
error_message String / Null Diễn giải lỗi dễ hiểu cho người dùng
Payload Output thực tế mẫu:
{
  "status": "success",
  "zalo_account_id": "zalo_acc_098765",
  "recipient_id": "zalo_uid_11223344",
  "thread_id": "thread_int_998877",
  "contact_id": "contact_665544",
  "friend_request": {
    "status": "skipped",
    "reason": "ALREADY_FRIEND",
    "id": null
  },
  "first_message": {
    "status": "success",
    "id": "msg_zalo_001122"
  },
  "message": {
    "status": "not_executed",
    "id": null
  },
  "contact_link": {
    "status": "success"
  },
  "error_code": null,
  "error_message": null
}

VI. Test Matrix (Chấp thuận nghiệm thu)

Bảng ma trận kiểm thử chuẩn hóa từ 21 tiêu chí nghiệm thu (AC01 – AC21):

Mã TC Hạng mục kiểm thử Dữ liệu đầu vào Kỳ vọng hệ thống
TC-01 Kiểm tra cấu trúc form Mở panel cấu hình node. Hiển thị đúng 3 tab (Hành động, Cài đặt, Đầu ra), không có tab Auth hay nút Lưu riêng.
TC-02 Chọn tài khoản Zalo Chọn tài khoản cố định hoặc map biến User. Hiển thị Avatar + Tên Zalo; runtime resolve chính xác tài khoản gửi.
TC-03 Bật/Tắt thao tác checkbox Tick chọn lần lượt 4 thao tác. Panel cấu hình tương ứng hiển thị/ẩn mượt mà; không có dropdown nguồn cũ.
TC-04 Chọn thread trực tiếp Chọn thread chat từ combobox picker. Lưu và thực thi trên đúng thread_id nội bộ của hội thoại đó.
TC-05 Nhập SĐT vào Thread ID (Happy Path) Nhập SĐT 0901234567 có tồn tại thread. Tự tra cứu và resolve đúng thread cá nhân trong tài khoản và workspace.
TC-06 Chuẩn hóa định dạng SĐT Nhập +84901234567 hoặc 0901234567. Tự chuẩn hóa và resolve về cùng một người nhận / thread.
TC-07 Cách ly dữ liệu giữa các workspace SĐT tồn tại ở workspace hoặc tài khoản khác. Không lấy thread ngoài phạm vi tài khoản và workspace hiện tại.
TC-08 Nhập SĐT chưa có thread Nhập SĐT chưa từng chat vào ô Thread ID. Báo THREAD_NOT_FOUND, không tự ý tạo thread hay gửi tin ngầm.
TC-09 SĐT xuất hiện trong nội dung chat SĐT chỉ xuất hiện dạng văn bản trong tin nhắn. Không coi đó là thread cá nhân phù hợp để resolve.
TC-10 Thread ID nhận từ biến chỉ gồm số Biến có giá trị "123456789" mang kiểu thread_id. Giữ nguyên kiểu thread_id, không suy diễn nhầm thành SĐT.
TC-11 Validate ô Người nhận & Thread ID Cả 2 ô trống hoặc cả 2 ô cùng có giá trị. Chặn validate và báo lỗi tương ứng (TARGET_REQUIRED hoặc TARGET_INPUT_CONFLICT).
TC-12 Thread nhóm + Kết bạn / Tin đầu tiên Chọn thread nhóm nhưng tick kết bạn/tin đầu. Báo lỗi PERSONAL_THREAD_REQUIRED, chỉ cho phép gửi tin vào hội thoại.
TC-13 Đã là bạn hoặc đang chờ duyệt Gửi kết bạn tới người đã là bạn. Trả friend_request.status = "skipped", không coi là lỗi làm dừng flow.
TC-14 Retry tin đầu tiên đã thành công Flow retry khi tin đầu đã gửi thành công. Bỏ qua không gửi lại tin đầu (skipped), giữ nguyên first_message.id cũ.
TC-15 Tin đầu thành công nhưng thread chưa sync Gửi tin đầu thành công nhưng sync trễ. Thao tác gửi tin thành công, các bước cần thread tạm dừng với THREAD_NOT_SYNCED.
TC-16 Tick cả 2 thao tác gửi tin Tick cả Gửi tin đầu tiên và Gửi tin hội thoại. Gửi tuần tự 2 tin riêng biệt theo thứ tự, ghi nhận 2 message ID độc lập.
TC-17 Liên kết liên hệ (Conflict Check) Thread đã gắn với Contact A, yêu cầu gắn Contact B. Báo lỗi CONTACT_LINK_CONFLICT, không tự động ghi đè liên kết.
TC-18 Bước liên kết lỗi sau khi gửi tin xong Gửi tin thành công nhưng Contact ID không tồn tại. Trả status = "partial_success", giữ nguyên kết quả các tin đã gửi thành công.
TC-19 Thay đổi tài khoản Zalo Đổi sang tài khoản Zalo khác sau khi đã chọn thread. Tự động xóa thread cũ không còn phù hợp, yêu cầu chọn lại.
TC-20 Timeout API Zalo chưa rõ kết quả Mạng chập chờn khi gọi API gửi tin. Trả status = "unknown", không tự động gửi lại để chống spam.
TC-21 Kiểm tra tab Đầu ra Xem danh sách output của node. Hiển thị đầy đủ icon kiểu dữ liệu + tên biến theo chuẩn Figma tham chiếu.