Ngoài các HTTP API mô phỏng Zalo OA, plugin web của Zalo Sandbox còn cung cấp một số màn hình giúp quan sát và thao tác trực tiếp với dữ liệu thử nghiệm. Thay vì chỉ gọi API rồi kiểm tra kết quả trong trang quản trị, lập trình viên có thể đóng vai OA và vai người dùng Zalo cùng lúc trên hai tab trình duyệt, gửi tin qua lại và theo dõi webhook được phát về ứng dụng.
Tài liệu này giới thiệu các màn hình đó, cách chúng kết nối với nhau và những API đứng phía sau.

Tổng quan

Plugin web có ba màn hình chính:
Đường dẫnMàn hìnhVai trò
/zalo-sandboxTrang giới thiệuĐiểm vào, dẫn đến các màn hình còn lại
/zalo-sandbox/oa/chatGiao diện chat OAĐóng vai quản trị viên OA, nhắn tin với người dùng OA
/zalo-sandbox/user/chatGiao diện chat người dùngĐóng vai người dùng Zalo: đăng ký, đăng nhập, nhắn với OA và với người dùng khác
Nếu website chưa có theme hoặc plugin nào xử lý đường dẫn gốc /, plugin sẽ tự đăng ký một handler chuyển hướng / sang /zalo-sandbox. Việc kiểm tra diễn ra một lần sau khi ứng dụng khởi động xong, nên khi cài thêm theme có trang chủ riêng thì cần khởi động lại để trang chủ đó được ưu tiên.
flowchart LR
    I[Trang giới thiệu] --> S[Swagger]
    I --> O[Chat OA]
    I --> U[Chat người dùng]
    O <-->|Tin nhắn trong kênh OA| U
    U <-->|Kênh chat trực tiếp| U2[Người dùng khác]
    U -->|Webhook user_send_*| H[Webhook của ứng dụng]

Trang giới thiệu

Screenshot 2026-09-23 at 18.34.36.png
Trang /zalo-sandbox hiển thị ba nút ở giữa màn hình, mỗi nút mở một tab mới:
  • Tài liệu Zalo Sandbox API: trang Swagger mô tả các endpoint mô phỏng Zalo OA.
  • Giao diện chat OA: mở /zalo-sandbox/oa/chat.
  • Giao diện chat người dùng: mở /zalo-sandbox/user/chat.

Giao diện chat OA

Screenshot 2026-09-23 at 18.34.58.png
Màn hình này mô phỏng giao diện Official Account Manager. Quản trị viên OA thấy danh sách người dùng OA ở cột trái và nội dung hội thoại với từng người ở khung bên phải.

Danh sách người dùng OA

Screenshot 2026-09-23 at 18.33.46.png
Danh sách gồm toàn bộ người dùng được gán vai trò zalo-oa-sandbox-user, được tải qua nhiều trang cho đến hết. Mỗi mục hiển thị ảnh đại diện, tên, thời gian và nội dung tin nhắn cuối. Danh sách được sắp theo tin nhắn mới nhất, người chưa có tin nhắn nằm cuối.
Tin nhắn cuối được hiển thị theo chiều gửi: tin do OA gửi có tiền tố OA:, tin do người dùng gửi thì không. Với tin có đính kèm, nội dung được thay bằng nhãn như [Hình ảnh], [Video], [Tệp]; tin template hiển thị [Tin mẫu].
Hai thao tác trên đầu danh sách:
  • Tìm kiếm (biểu tượng kính lúp): lọc người dùng OA theo từ khóa. Hệ thống lần lượt thử khớp chính xác username, số điện thoại, email, UUID, sau đó tìm theo tiền tố từ khóa.
  • Thêm người dùng OA (biểu tượng thêm người): mở hộp thoại tìm những người dùng chưa phải người dùng OA và gán vai trò cho họ. Người vừa thêm được đưa lên đầu danh sách và mở hội thoại ngay.

Hội thoại

Khi chọn một người dùng, khung chat tải 30 tin nhắn mới nhất; cuộn lên đầu để tải thêm tin cũ. Tin do OA gửi nằm bên phải, tin do người dùng gửi nằm bên trái, các ngày khác nhau được ngăn cách bằng dải ngày.
Thanh công cụ soạn tin hỗ trợ:
  • nhập văn bản và nhấn Enter hoặc nút gửi;
  • chèn biểu tượng cảm xúc từ bảng emoji;
  • gửi ảnh, video hoặc tệp bất kỳ.
Tin do quản trị viên gửi từ màn hình này được lưu giống hệt tin gửi qua API /v3.0/oa/message/cs. Tin văn bản có dạng {"text": ...}; tin đính kèm dùng cấu trúc attachment.payload.elements với media_type, url, attachment_id và file_name. Nhờ vậy ứng dụng và màn hình chat người dùng đọc được chúng theo cùng một định dạng.

Giao diện chat người dùng

Màn hình này mô phỏng Zalo Web ở phía người dùng. Đăng nhập và chat nằm trong cùng một trang: khi chưa đăng nhập, trang hiển thị màn hình đăng nhập; khi đã đăng nhập, trang chuyển sang màn hình chat mà không tải lại.

Đăng nhập

Khi mở trang, hệ thống gọi PUT /ezylogin/api/v1/access-token/renew của plugin ezylogin để kiểm tra cookie đăng nhập. Nếu token còn tồn tại, trang vào thẳng màn hình chat và phiên đăng nhập được gia hạn; nếu không, màn hình đăng nhập được hiển thị. Vì vậy làm mới trang không buộc người dùng đăng nhập lại.
Người dùng có thể nhập số điện thoại hoặc tên đăng nhập:
  • Nếu giá trị chỉ gồm 9 đến 11 chữ số (sau khi bỏ khoảng trắng, dấu chấm, dấu gạch), hệ thống coi đó là số điện thoại. Số chưa có 0 ở đầu sẽ được thêm 0; hệ thống không tự thêm +84.
  • Các giá trị khác được gửi nguyên văn như tên đăng nhập.
Việc xác thực được thực hiện bằng POST /ezylogin/api/v1/login. Sai thông tin hiển thị thông báo tài khoản hoặc mật khẩu không đúng.

Đăng ký

Liên kết Đăng ký dưới form đăng nhập mở form tạo tài khoản gồm số điện thoại, họ và tên, mật khẩu và nhập lại mật khẩu. API POST /api/v1/zalo-sandbox/user/register kiểm tra:
  • số điện thoại hợp lệ và chưa được đăng ký;
  • họ và tên không rỗng, tối đa 128 ký tự;
  • mật khẩu dài từ 6 đến 128 ký tự và khớp với ô nhập lại.
Tài khoản được tạo ở trạng thái đã kích hoạt, email được sinh ngẫu nhiên dạng zalo_sandbox_<uuid>@zalo-sandbox.local và tên đăng nhập do EzyPlatform tự sinh. Sau khi đăng ký thành công, trang tự đăng nhập bằng số điện thoại và mật khẩu vừa nhập rồi chuyển sang màn hình chat.

Danh sách hội thoại

Mục đầu tiên luôn là Zalo Sandbox - OA, được ghim cố định để việc thử gửi và nhận tin OA thuận tiện. Bên dưới là các kênh chat trực tiếp giữa người dùng hiện tại với người dùng khác, sắp theo thời gian trò chuyện gần nhất. Mỗi kênh hiển thị tên và ảnh đại diện của người còn lại, tin nhắn cuối và số tin chưa đọc.
Các tab Tất cả và Chưa đọc cùng ô Tìm kiếm lọc danh sách ngay trên trình duyệt.
Biểu tượng thêm người mở hộp thoại Tạo cuộc trò chuyện: người dùng tìm một tài khoản đang hoạt động khác rồi nhấn Nhắn tin. Mỗi cặp người dùng chỉ có một kênh trực tiếp, được đặt tên cố định zalo_sandbox_direct_{idNhỏ}_{idLớn}; nếu kênh đã tồn tại thì hệ thống mở lại kênh cũ.

Nhắn tin

Màn hình chat hỗ trợ:
  • gửi văn bản bằng Enter hoặc nút gửi;
  • gửi 👍 khi nhấn nút gửi lúc ô nhập đang trống;
  • chèn biểu tượng cảm xúc từ bảng emoji;
  • gửi ảnh hoặc video (biểu tượng hình ảnh) và tệp bất kỳ (biểu tượng kẹp giấy).
Ảnh hiển thị dạng hình thu nhỏ, video có nút phát, tệp hiển thị biểu tượng và tên tệp. Biểu tượng 👍 xuất hiện khi rê chuột lên một tin nhắn hiện chỉ là tương tác trên giao diện, chưa được lưu.
Tin nhắn trong kênh trực tiếp được tải qua API sẵn có của ezychat. API này cũng đánh dấu kênh là đã đọc. Tin gửi đi đi qua API của Zalo Sandbox và được xử lý tương tự luồng socket của ezychat: lưu tin và media, cập nhật thời gian trò chuyện, tăng số chưa đọc của người nhận và tạo trạng thái đã gửi.

Nhắn tin với OA

Tin người dùng gửi cho OA được lưu vào kênh chat OA với các loại riêng USER_SEND_TEXT, USER_SEND_IMAGE, USER_SEND_VIDEO và USER_SEND_FILE, để phân biệt với tin do OA gửi. Trên màn hình chat OA, các tin này hiển thị ở phía người dùng.
Sau khi lưu, sandbox phát webhook đến toàn bộ URL webhook đã cấu hình với tên sự kiện tương ứng:
Nội dung gửiSự kiệnTrường chính trong `message`
Văn bản, emoji, 👍user_send_textmsg_id, text
Ảnhuser_send_imagemsg_id, attachments
Videouser_send_videomsg_id, attachments
Tệpuser_send_filemsg_id, attachments
Payload và chữ ký X-ZEvent-Signature dùng cùng cơ chế với chức năng phát webhook thủ công trong trang quản trị. Webhook được gửi bất đồng bộ nên việc gửi tin không bị chậm khi URL đích phản hồi chậm; lỗi phát webhook chỉ được ghi log.
Nếu người dùng chưa có vai trò zalo-oa-sandbox-user, lần đầu nhắn cho OA hệ thống sẽ tự gán vai trò này để người dùng xuất hiện trong danh sách của màn hình chat OA.

Cài đặt và đăng xuất

Biểu tượng bánh răng ở cuối thanh bên mở menu cài đặt, trong đó có mục Đăng xuất. Mục này gọi POST /logout của ezylogin để xóa cookie đăng nhập, sau đó trang trở về màn hình đăng nhập.

Cập nhật dữ liệu gần thời gian thực

Hai màn hình chat không dùng socket. Thay vào đó, sandbox duy trì trong bộ nhớ các mốc thời gian thay đổi:
  • một mốc chung cho kênh OA, dùng bởi màn hình chat OA;
  • một mốc riêng cho từng người dùng, dùng bởi màn hình chat người dùng.
Mỗi giây, trình duyệt gọi API state tương ứng. API này chỉ đọc bộ nhớ nên rất nhẹ. Khi mốc thời gian thay đổi, trình duyệt mới gọi các API tải tin nhắn của hội thoại đang mở và làm mới danh sách hội thoại.
sequenceDiagram
    participant A as Người gửi
    participant S as Zalo Sandbox
    participant B as Trình duyệt người nhận
    A->>S: Gửi tin nhắn
    S->>S: Lưu tin, cập nhật mốc thời gian
    loop Mỗi giây
        B->>S: GET state
        S-->>B: updatedAt
    end
    B->>S: Mốc thay đổi, tải tin nhắn mới
Các thao tác cập nhật mốc thời gian gồm: OA gửi tin (qua API hoặc màn hình chat OA), thêm người dùng OA, người dùng gửi tin cho OA, gửi tin trong kênh trực tiếp và tạo kênh trực tiếp.

Danh sách API phục vụ các màn hình

Phương thứcEndpointĐăng nhậpMục đích
GET/api/v1/zalo-sandbox/oa/stateKhôngMốc thay đổi của kênh OA
GET/api/v1/zalo-sandbox/oa/usersKhôngDanh sách người dùng OA, hỗ trợ keyword
POST/api/v1/zalo-sandbox/oa/usersKhôngGán vai trò người dùng OA
GET/api/v1/zalo-sandbox/usersKhôngTìm người dùng chưa phải người dùng OA
GET/api/v1/zalo-sandbox/oa/users/{userId}/messagesKhôngTin nhắn giữa OA và một người dùng
POST/api/v1/zalo-sandbox/oa/users/{userId}/messagesKhôngOA gửi tin văn bản
POST/api/v1/zalo-sandbox/oa/users/{userId}/media-messagesKhôngOA gửi ảnh, video, tệp
POST/api/v1/zalo-sandbox/oa/users/{userId}/readKhôngOA đánh dấu đã đọc hội thoại với một người dùng
POST/api/v1/zalo-sandbox/user/registerKhôngĐăng ký tài khoản
GET/api/v1/zalo-sandbox/user/meCóThông tin người dùng hiện tại
GET/api/v1/zalo-sandbox/user/stateCóMốc thay đổi của người dùng hiện tại
GET/api/v1/zalo-sandbox/user/direct-channelsCóDanh sách kênh chat trực tiếp
POST/api/v1/zalo-sandbox/user/direct-channelsCóTạo hoặc mở lại kênh trực tiếp
GET/api/v1/zalo-sandbox/user/usersCóTìm người dùng để bắt đầu trò chuyện
POST/api/v1/zalo-sandbox/user/direct-channels/{channelId}/messagesCóGửi tin văn bản trong kênh trực tiếp
POST/api/v1/zalo-sandbox/user/direct-channels/{channelId}/media-messagesCóGửi ảnh, video, tệp trong kênh trực tiếp
GET/api/v1/zalo-sandbox/user/oa/messagesCóTin nhắn giữa người dùng hiện tại và OA
POST/api/v1/zalo-sandbox/user/oa/messagesCóNgười dùng gửi tin văn bản cho OA
POST/api/v1/zalo-sandbox/user/oa/media-messagesCóNgười dùng gửi ảnh, video, tệp cho OA
Các API danh sách dùng cơ chế phân trang của EzyPlatform với nextPageToken, prevPageToken, lastPage và limit. Các API gửi media nhận multipart với trường file.
Ngoài ra, màn hình chat người dùng sử dụng các API của plugin khác: đăng nhập, gia hạn token và đăng xuất của ezylogin, cùng API tải tin nhắn kênh GET /ezychat/api/v1/channels/{id}/messages của ezychat.

Một kịch bản thử nghiệm

  1. Mở /zalo-sandbox, chọn Giao diện chat người dùng và đăng ký một tài khoản mới.
  2. Trong mục Zalo Sandbox - OA, gửi một tin văn bản và một ảnh.
  3. Kiểm tra ứng dụng nhận được webhook user_send_text và user_send_image với chữ ký hợp lệ.
  4. Mở Giao diện chat OA ở tab khác; người dùng vừa đăng ký xuất hiện trong danh sách cùng các tin vừa gửi.
  5. Trả lời từ màn hình chat OA, hoặc từ ứng dụng thông qua /v3.0/oa/message/cs; tin trả lời hiện ở màn hình chat người dùng sau khoảng một giây.
  6. Đăng ký thêm một tài khoản thứ hai, dùng Tạo cuộc trò chuyện để nhắn tin trực tiếp giữa hai tài khoản.

Giới hạn cần lưu ý

  • Các màn hình chat OA và phần lớn API phía OA không yêu cầu đăng nhập; chỉ nên dùng trong môi trường thử nghiệm.
  • Mốc thời gian thay đổi được lưu trong bộ nhớ của một tiến trình. Khi chạy nhiều instance, trình duyệt có thể không nhận được thay đổi xảy ra ở instance khác. Khi khởi động lại, mỗi trang đang mở sẽ làm mới dữ liệu một lần.
  • Tin nhắn gửi qua socket của ezychat hoặc qua plugin khác không cập nhật mốc thời gian, nên chỉ hiển thị khi có thay đổi khác hoặc khi tải lại trang.
  • Tương tác 👍 trên từng tin nhắn và các biểu tượng trang trí (sticker, danh thiếp, định dạng văn bản...) chưa có chức năng.
  • Đăng nhập qua mã QR chỉ là hình minh họa.
  • Người dùng mới đăng ký chỉ được gán vai trò người dùng OA khi lần đầu nhắn cho OA.