← Tất cả bài viết
Hướng dẫn

Cách đồng bộ trạng thái đã đọc và chưa đọc WhatsApp trong hộp thư dùng chung

Hộp thư WhatsApp dùng chung không nên đánh dấu cuộc trò chuyện là đã đọc ngay khi Webhook đến. Chỉ cập nhật khi nhóm thực sự nhận việc hoặc xử lý xong. Với UnifyPort, bạn gọi thao tác đọc bằng conversation_id; nếu muốn gửi biên nhận đến đúng một tin nhắn WhatsApp, phải truyền đồng thời ID tin nhắn và ID người gửi. Khi cần theo dõi lại, hãy đánh dấu cuộc trò chuyện là chưa đọc.

Điểm chính

  • Thao tác đọc và chưa đọc ở cấp cuộc trò chuyện hiện chỉ hỗ trợ WhatsApp. Tổ hợp nhà cung cấp và thao tác không được hỗ trợ trả về 501 unsupported_by_provider.
  • POST /v1/accounts/{account_id}/conversations/read nhận conversation_id bắt buộc và có thể nhận mốc tin nhắn cụ thể.
  • up_to_message_idup_to_message_sender_id phải đi cùng nhau. Chỉ gửi một trường sẽ nhận 400 invalid_request.
  • Thao tác đánh dấu chưa đọc chỉ cần conversation_id.
  • Hãy lưu trạng thái phân công, chờ xử lý và hoàn tất trong hệ thống riêng. Trạng thái phía WhatsApp chỉ là một hình chiếu, không phải toàn bộ cơ sở dữ liệu hỗ trợ.

Tách ba ý nghĩa của “đã đọc”

Một hộp thư dùng chung ổn định phải phân biệt ba trạng thái:

  1. Trạng thái hàng đợi nội bộ: mới, đã phân công, đang chờ, đã giải quyết hoặc các bước do ứng dụng của bạn định nghĩa.
  2. Trạng thái danh sách chat của tài khoản WhatsApp đã kết nối: đã đọc hoặc chưa đọc. Thay đổi qua API đánh dấu cuộc trò chuyện đã đọcAPI đánh dấu chưa đọc.
  3. Biên nhận của người nhận: Webhook message.read cho biết người nhận đã đọc một hoặc nhiều tin nhắn được gửi từ tài khoản. Nó không có nghĩa nhân viên đã mở phiếu yêu cầu đến.

Khi thiết lập cục bộ của cuộc trò chuyện thay đổi, UnifyPort có thể ánh xạ sự kiện conversation.updated. data.conversation.id xác định cuộc chat, còn thay đổi trạng thái đọc có thể nằm ở data.read. Hãy dùng sự kiện này để đối soát; ai nhận việc, khi nào đóng và lý do mở lại vẫn phải được lưu trong cơ sở dữ liệu của bạn.

Trước khi áp dụng sự kiện, hãy xác minh chữ ký trên raw body và xử lý lần gửi lại theo cách idempotent. Xem ranh giới bộ nhận trong hướng dẫn Webhook HMAC, chống replay và retry. Nếu endpoint chỉ cần sự kiện hộp thư, hãy đăng ký rõ các sự kiện cần thiết theo hướng dẫn bộ lọc sự kiện Webhook.

Chọn thời điểm đổi trạng thái phía WhatsApp

Không tự động đánh dấu đã đọc cho mọi Webhook đến. Nếu làm vậy, một hàng đợi chưa ai xử lý vẫn trông như đã sạch. Hãy đặt chính sách rõ ràng:

Hành động của nhómTrạng thái nội bộHành động WhatsApp
Đã lưu tin nhắn đếnnewKhông
Nhân viên nhận cuộc trò chuyệnassignedCó thể đánh dấu đến tin nhắn đã nhận
Nhân viên giải quyết xongresolvedĐánh dấu toàn bộ cuộc trò chuyện đã đọc
Cần theo dõi lạiwaitingĐánh dấu cuộc trò chuyện chưa đọc
Tự động hóa lỗi trước khi phân côngnewKhông

Việc tách trạng thái cũng ngăn thao tác tải lại trình duyệt, retry Webhook hoặc bản xem trước nền vô tình xóa công việc đang chờ.

Đánh dấu cuộc trò chuyện WhatsApp là đã đọc

Gửi conversation_id trong JSON body thay vì URL path vì ID của nhà cung cấp có thể chứa ký tự như @ hoặc :.

Đánh dấu toàn bộ cuộc trò chuyện:

curl -X POST "https://api.unifyport.ai/v1/accounts/$UNIFYPORT_ACCOUNT_ID/conversations/read" \
  -H "X-Api-Key: $UNIFYPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "conversation_id": "8613912345678@s.whatsapp.net"
  }'

Để gửi biên nhận đến một tin nhắn đến cụ thể, hãy lấy cả ba ID từ cùng sự kiện message.received:

{
  "conversation_id": "120363041234567890@g.us",
  "up_to_message_id": "CURRENT-MESSAGE-ID",
  "up_to_message_sender_id": "8613912345678@lid"
}

Trong nhóm, up_to_message_sender_id phải là data.sender.id tương ứng. Không suy ra nó từ conversation ID. Nếu không cần biên nhận theo tin nhắn, hãy bỏ cả hai trường up_to_message_*.

Hàm Node.js nhỏ dưới đây kiểm tra cặp trường trước khi gọi API:

const apiBase = 'https://api.unifyport.ai/v1';

async function setWhatsAppReadState({ accountId, conversationId, unread, message }) {
  const action = unread ? 'unread' : 'read';
  const body = { conversation_id: conversationId };

  if (!unread && message) {
    if (!message.id || !message.senderId) {
      throw new Error('message.id and message.senderId must be supplied together');
    }
    body.up_to_message_id = message.id;
    body.up_to_message_sender_id = message.senderId;
  }

  const response = await fetch(
    `${apiBase}/accounts/${encodeURIComponent(accountId)}/conversations/${action}`,
    {
      method: 'POST',
      headers: {
        'X-Api-Key': process.env.UNIFYPORT_API_KEY,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(body)
    }
  );

  if (!response.ok) {
    const failure = await response.json();
    throw new Error(`${response.status} ${failure.error?.code ?? 'unknown_error'}`);
  }

  return response.json();
}

Trong event handler đã xác minh chữ ký, dùng trực tiếp các trường được tài liệu hóa:

await setWhatsAppReadState({
  accountId: event.account_id,
  conversationId: event.data.conversation.id,
  unread: false,
  message: {
    id: event.data.message.id,
    senderId: event.data.sender.id
  }
});

Đánh dấu chưa đọc để theo dõi lại

Thao tác chưa đọc ngắn hơn:

await setWhatsAppReadState({
  accountId: event.account_id,
  conversationId: event.data.conversation.id,
  unread: true
});

Chỉ dùng khi nhóm chủ động mở lại công việc. Không dùng trạng thái chưa đọc của nhà cung cấp làm cơ chế nhắc việc duy nhất; hãy lưu người phụ trách, trạng thái hạn xử lý và lý do trong hàng đợi nội bộ.

Đối soát mà không tạo vòng lặp

Sau khi ứng dụng gọi thao tác cuộc trò chuyện, Webhook có thể nhận conversation.updated tương ứng. Hãy ghi lại thao tác do hệ thống của bạn khởi tạo để sự kiện đến sau xác nhận trạng thái, thay vì kích hoạt lại cùng thao tác.

Quy trình an toàn:

  1. Lưu message.received theo cách idempotent.
  2. Cập nhật trạng thái phiếu nội bộ trong transaction.
  3. Gọi thao tác trạng thái phía nhà cung cấp.
  4. Chỉ ghi thành công sau khi API trả { "data": { "ok": true } }.
  5. Xử lý conversation.updated như xác nhận hoặc thay đổi bên ngoài từ tài khoản đã kết nối.
  6. Khi chưa chắc chắn, gọi Get conversation cho đúng cuộc trò chuyện và so sánh unread_count.

Trước khi bật nút điều khiển cho kênh khác, hãy kiểm tra ma trận thao tác theo nhà cung cấp. Một route thống nhất không có nghĩa mọi nhà cung cấp hỗ trợ mọi thao tác.

Giới hạn và lựa chọn

Kết nối chính thức có thể phù hợp hơn nếu quy trình phụ thuộc vào tính năng doanh nghiệp được chứng nhận, tin nhắn mẫu chính thức hoặc quản trị gốc của nền tảng. Giao diện không chính thức của UnifyPort phù hợp với luồng nhắn tin từ tài khoản thông thường, nhưng không làm cho bộ tính năng của mọi kênh giống hệt nhau.

Trong quy trình này, read/unread hiện chỉ áp dụng cho WhatsApp. Với nhóm tại Việt Nam dùng cả Zalo và WhatsApp, hãy giữ trạng thái hàng đợi Zalo trong ứng dụng và ẩn nút chỉ dành cho WhatsApp. Không nên coi 501 unsupported_by_provider là lỗi tạm thời để retry liên tục.

Câu hỏi thường gặp

Nhận message.received có tự động đánh dấu chat đã đọc không?

Không. Việc nhận và lưu sự kiện không nên xóa hàng đợi. Chỉ gọi endpoint read tại bước nghiệp vụ mà nhóm đã chọn.

message.read khác gì với đánh dấu cuộc trò chuyện đã đọc?

message.read là biên nhận cho biết người nhận đã đọc tin nhắn gửi đi. Thao tác cuộc trò chuyện thay đổi trạng thái danh sách chat cục bộ của tài khoản đã kết nối.

Có thể chỉ gửi up_to_message_id không?

Không. Phải gửi cùng up_to_message_sender_id, hoặc bỏ cả hai để đánh dấu toàn bộ cuộc trò chuyện.

Có thể dùng cùng thao tác cho Telegram, LINE, TikTok, Zalo và X không?

Hiện chưa thể. Ma trận hỗ trợ liệt kê read và unread ở cấp cuộc trò chuyện chỉ dành cho WhatsApp. Tổ hợp không hỗ trợ trả về 501 unsupported_by_provider.

Bước tiếp theo

Bắt đầu với API Reference đánh dấu cuộc trò chuyện đã đọc, sau đó chỉ thêm thao tác chưa đọc khi chính sách mở lại nội bộ đã rõ ràng.

Nguồn chính thức

Đã kiểm tra ngày 21 tháng 8 năm 2026: