← Tất cả bài viết
Nhật ký thay đổi

UnifyPort v1 API đã sẵn sàng cho production: Tất cả những gì có trong bản ổn định đầu tiên — UnifyPort

UnifyPort v1 API hiện đã sẵn sàng cho production. Đây là bản ổn định đầu tiên: một endpoint gửi, một lớp sự kiện Webhook được chuẩn hóa, và các luồng kết nối tài khoản cho cả sáu kênh — WhatsApp, Telegram, LINE, TikTok, Zalo và X — phía sau một bề mặt API duy nhất.

Bài viết này là nhật ký thay đổi của bản phát hành đó. Nếu bạn đang chờ một phiên bản có thể xây dựng lên trên mà không lo nền móng bên dưới đột ngột thay đổi gây hỏng, thì đây chính là nó. Dưới đây là những gì đã ra mắt, theo từng endpoint, kèm tên sự kiện thực tế, luồng xác thực và hành vi độ tin cậy mà bạn sẽ làm việc cùng.

Một API gửi cho sáu kênh

Cốt lõi của v1 là một endpoint. POST /v1/messages gửi một tin nhắn văn bản đã chuẩn hóa qua tài khoản provider mà bạn chọn. Bạn không cần học sáu SDK hay sáu định dạng payload — chỉ một mà thôi.

Hành vi đặc thù từng nền tảng không biến mất; nó chuyển vào trường có cấu trúc provider_data. Cần trả lời một tin nhắn cụ thể, hay đặt parse mode của Telegram? Những thứ đó nằm trong provider_data (ví dụ reply_toparse_mode) mà không thay đổi cấu trúc của request ở cấp cao nhất. Trường hợp phổ biến vẫn đơn giản; trường hợp đặc thù nền tảng vẫn khả thi.

Sáu kênh được hỗ trợ là WhatsApp, Telegram, LINE, TikTok, Zalo và X. Trong API, tài khoản provider được tạo với các tên như telegram, whatsapp, twitter, linezalo.

Với các đội ở Việt Nam thường chạy song song cả WhatsApp lẫn Zalo, đây là điểm mấu chốt: tin nhắn đến từ Zalo và từ WhatsApp đều đến cùng một endpoint với cùng một schema, nên logic xử lý là chung.

11 sự kiện Webhook chuẩn

Nửa còn lại của v1 là lớp sự kiện chuẩn. Mỗi tin nhắn từ provider, mỗi cập nhật trạng thái gửi và mỗi sự kiện vòng đời tài khoản đều được chuyển thành một hình dạng JSON ổn định duy nhất. Bản phát hành này có 11 sự kiện chuẩn, và backend của bạn xử lý tất cả qua cùng một handler.

Hai sự kiện bạn sẽ dùng nhiều nhất:

  • message.received — một tin nhắn đến đã tới một tài khoản đang kết nối
  • message.status.updated — trạng thái gửi của một tin nhắn bạn đã gửi thay đổi

Các sự kiện vòng đời tài khoản khiến trạng thái kết nối có thể quan sát được, thay vì mờ mịt:

  • account.auth.required — tài khoản cần một thao tác đăng nhập (quét QR hoặc mã xác minh)
  • account.auth.succeeded — đăng nhập hoàn tất; provider_account_ref giờ đã được điền, và sự kiện mang theo các trường hồ sơ chuẩn (display_name, picture_url, region, status_message)
  • account.started — runtime của tài khoản đã trực tuyến
  • account.status.updated — trạng thái tài khoản thay đổi

Sự kiện message.received trông giống nhau bất kể nó đến từ kênh nào trong sáu kênh:

{
  "event": "message.received",
  "account_id": "acct_8Q2vK",
  "provider": "zalo",
  "from": "user_8f3a2c",
  "text": "Cho mình hỏi đơn hàng đã sẵn sàng chưa?",
  "timestamp": 1749254400,
  "message_id": "zalo_msg_8f3a2c"
}

Đổi zalo thành whatsapp hay line, logic định tuyến của bạn không thay đổi một dòng nào. Đó chính là mục đích của lớp chuẩn.

Kết nối tài khoản: luồng QR và mã

Kết nối tài khoản là một luồng riêng trong v1, được thiết kế sao cho trạng thái xác thực là thứ Webhook của bạn theo dõi được, chứ không phải thứ bạn phải canh chừng thủ công.

Bạn tạo tài khoản bằng POST tới endpoint tài khoản, chọn auth_mode phù hợp với kiểu kết nối:

{
  "provider": "whatsapp",
  "auth_mode": "code",
  "provider_data": { "phone": "+84901234567" }
}

Với luồng dùng mã, số điện thoại định dạng E.164 được cung cấp qua provider_data.phone. Nó được lưu cố định trên tài khoản và tự động phát lại cho các thao tác xác thực sau, nên bạn không phải gửi lại ở mỗi bước. Tạo một định danh provider trùng lặp sẽ trả về 409 duplicate_provider_account rõ ràng, thay vì âm thầm tạo tài khoản thứ hai.

Với luồng dùng QR, thông tin xác thực đến bất đồng bộ qua Webhook của bạn:

  • WhatsApp — gọi /auth/qr/start để bắt đầu phiên thiết bị. Token QR sẽ tới Webhook dưới dạng sự kiện account.auth.required (auth_payload.qr_code là token thô mà UI của bạn cần render thành ảnh QR), và cũng được phơi bày đồng bộ qua /auth/qr/check để poll. Sau khi quét, account.auth.succeeded đến với provider_account_ref đã được điền, tiếp theo là account.started.
  • LINE — URL QR và PIN đến bất đồng bộ. Lắng nghe account.auth.required, hoặc poll /auth/session để đọc auth_payload.url (ảnh QR) và auth_payload.pin.

URL Webhook được tự động chèn vào khi cấu hình tài khoản — các chuyển trạng thái xác thực và sự kiện đăng nhập đến cùng endpoint với tin nhắn đến. Một Webhook, mọi sự kiện.

Khi account.auth.succeeded tới, POST /v1/accounts/{account_id}/runtime/start đưa tài khoản trực tuyến (là no-op nếu runtime đã chạy).

Lớp điều khiển Webhook

Endpoint Webhook trong v1 là đối tượng hạng nhất, có thể cấu hình — không phải một trường URL đơn lẻ chôn trong phần cài đặt.

Khi tạo bộ nhận Webhook, bạn chọn chính xác nó nhận sự kiện nào. subscribed_events chấp nhận tên sự kiện chuẩn (như message.received, account.status.updated), hoặc ["*"] để đăng ký tất cả. Tên sự kiện không xác định bị từ chối ngay khi ghi, thay vì âm thầm thất bại về sau.

Ký số là tính năng tích hợp. Cung cấp signing_secret và mọi lần gửi đều được ký bằng HMAC-SHA256 để endpoint của bạn xác minh tính xác thực. Để trống thì ký số bị tắt một cách tường minh — đây là lựa chọn có chủ ý, không phải sơ suất.

Hành vi thử lại do bạn đặt. retry_policy.max_attempts là số nguyên không âm; 0 tắt hoàn toàn việc thử lại, giá trị không phải số nguyên bị từ chối. Nền tảng lo việc gửi lại; endpoint của bạn chỉ cần phản hồi.

Độ tin cậy giữ nhiễu loạn trong lớp nền tảng

Một bản phát hành production cần các mặc định production, và v1 đi kèm chúng. Việc gửi tin đến chạy qua một hàng đợi có giới hạn tốc độ, thử lại, idempotency và cô lập lỗi, nên nhiễu loạn phía provider được giữ bên trong lớp nền tảng thay vì lan ra ứng dụng của bạn.

Trạng thái runtime của tài khoản cũng được chuẩn hóa. runtime_status nhận một trong tám giá trị chuẩn của nền tảng — unknown, starting, running, stopping, stopped, reconnecting, disconnected, error — và các nhãn đặc thù provider được ánh xạ vào tập này trước khi bạn nhìn thấy. Bạn viết một máy trạng thái, không phải sáu.

Với các tài khoản cần định tuyến theo địa lý, đối tượng tùy chọn proxy_config được lưu cùng tài khoản và áp dụng tự động.

Bản phát hành này có ý nghĩa gì

v1 sẵn sàng production là một cam kết, không chỉ là một con số phiên bản. Endpoint gửi, 11 sự kiện chuẩn, các luồng xác thực và lớp điều khiển Webhook là bề mặt ổn định mà bạn có thể xây sản phẩm lên trên. Mục đích của UnifyPort luôn là để các đội không phải xây lại cùng một tích hợp sáu lần; v1 là phiên bản mà bề mặt đó ngừng thay đổi.

Nếu bạn đã đọc các bài khác của chúng tôi về nhận tin nhắn mà không gặp ma sát của API chính thức — nhận WhatsApp đến mà không cần API chính thức, nhận tin nhắn LINE mà không cần Official Account, hoặc so sánh chi phí WhatsApp và Telegram — thì đây chính là API mà những con đường đó chạy trên.

Bắt đầu: tạo một API key, kết nối tài khoản đầu tiên với auth_mode phù hợp với kênh, đăng ký một endpoint Webhook với signing_secret, và đăng ký nhận message.received. Ngay trong cùng phiên làm việc, một sự kiện đến đã chuẩn hóa sẽ tới backend của bạn. Từ đó, mỗi kênh bổ sung đều là cùng một luồng — không phải thêm một dự án tích hợp nữa.