Hai chiều dùng chung một khóa bí mật và chung một cách ký, nên bạn chỉ phải viết hàm xác thực chữ ký một lần và dùng lại cho cả chiều gửi lẫn chiều nhận.
Chọn kênh tích hợp
Hai kênh không loại trừ nhau. Cùng một Agent có thể vừa phục vụ widget trên website, vừa phục vụ hệ thống của bạn qua kênh API. Mỗi kênh là một kết nối riêng với khóa riêng.
Môi trường và tên miền
Mọi thành phần nằm chung một tên miền ứng dụng, phân biệt bằng đường dẫn: webhook nhận tin nhắn của kênh API là
https://console-agents.fpt.ai/webhooks/api, còn API đọc lịch sử là https://console-agents.fpt.ai/direct-bff/…. Hãy sao chép đúng địa chỉ webhook từ Console thay vì tự suy ra.Kết nối kênh API và lấy khóa
1
Đăng nhập FPT AI Agent Platform
Mở Console và đăng nhập bằng tài khoản của bạn.
2
Chọn Agent bạn muốn cấu hình
Agent này sẽ trả lời tin nhắn mà hệ thống của bạn chuyển tới.
3
Mở tab Channels
Tab Channels (Kênh triển khai) nằm trên thanh điều hướng của Agent.
4
Chọn ô API
Bảng cấu hình kênh API mở ra.
5
Điền cấu hình của kênh
Hai trường, xem bảng ngay dưới.
6
Bấm Save configuration để tạo kênh
Ô API trong tab Channels chuyển sang Đã cấu hình.
7
Bấm Tạo khóa mới
Sao chép Khóa định tuyến và Khóa ký trước khi đóng hộp thoại.

Các giá trị bạn nhận được
Kiểm tra Callback URL
Nút Kiểm tra callback bên cạnh ô nhập gửi một yêu cầu đã ký tới địa chỉ của bạn và yêu cầu bạn trả lời đúng. Xem cách hiện thực ở mục Kiểm chứng điểm cuối webhook. Nút này giới hạn 10 lần kiểm tra mỗi phút cho mỗi tổ chức.Xoay vòng khóa bí mật
1
Bấm Tạo khóa mới
Kể từ lúc này, cả khóa cũ lẫn khóa mới đều được chấp nhận.
2
Cập nhật khóa mới lên toàn bộ máy chủ của bạn
Kiểm tra lại luồng gửi và luồng nhận.
3
Bấm Kết thúc vòng đổi khóa
Khóa cũ bị vô hiệu hóa. Từ thời điểm này chỉ khóa mới còn giá trị.
Xác thực nội dung sự kiện
Mọi gói tin ở cả hai chiều đều được ký bằng HMAC-SHA256 với khóa bí mật của kênh. Chữ ký đi trong headerX-Hub-Signature-256 với tiền tố sha256=, kèm header X-Hub-Timestamp mang thời điểm gửi.
Chuỗi được ký là timestamp + dấu chấm + phần thân nguyên bản:
- Ký trên đúng chuỗi byte đã gửi đi. Chuyển JSON thành đối tượng rồi tuần tự hóa lại, kể cả khi chỉ đổi thứ tự khóa, sẽ cho ra chữ ký khác.
- Timestamp nằm trong chữ ký, nên kẻ bắt được gói tin không thể sửa nó. Gửi thời gian Unix hiện tại tính bằng giây.
- Timestamp lệch quá 5 phút so với giờ máy chủ, theo cả hai hướng, đều bị từ chối. Hãy đồng bộ đồng hồ máy chủ bằng NTP.
- Cơ chế không có ngoại lệ. Không có chế độ bỏ qua chữ ký, và không có miễn trừ cho kết nối chưa tạo khóa.
Ví dụ tạo chữ ký với Go
Ví dụ tạo chữ ký với Node.js
Xác thực chữ ký nền tảng gửi tới
Dùng lại đúng hàm trên, rồi so sánh bằng hàm so sánh thời gian hằng số để không rò rỉ thông tin qua thời gian xử lý:Luồng nhận tin nhắn từ khách hàng
Tin nhắn khách hàng gửi tới hệ thống của bạn được chuyển tiếp tới nền tảng qua webhook. Sự kiện gồm hai phần: nội dung sự kiện và chữ ký xác thực nội dung sự kiện.Tin nhắn văn bản
Tin nhắn khi khách hàng bấm nút hoặc quick reply
Tin nhắn có tệp đính kèm
Bảng tham số
Trường
postback tới Agent như ngữ cảnh riêng, không bao giờ bị coi là lời khách hàng nói. Nhờ đó Agent phân biệt được điều gì do người gõ và điều gì do giao diện của bạn mang theo. Một trường duy nhất dùng cho cả nút bấm lẫn quick reply.Ví dụ gọi bằng cURL
Mã phản hồi
200 nghĩa là đã tiếp nhận, không phải đã trả lời. Agent xử lý bất đồng bộ và câu trả lời tới webhook của bạn sau đó.
Chống trùng lặp
messageId là khóa chống trùng đầu-cuối. Một tin nhắn cùng cặp (integrationKey, messageId) gửi lại trong vòng 5 phút sẽ được nhận ra và bỏ qua, nên việc gửi lại sau khi hết thời gian chờ không làm khách hàng của bạn nhận hai câu trả lời.
Tệp đính kèm gửi lên
Mỗi mục trongattachments cần một url mà nền tảng tải được. Nội dung tệp được sao chép vào kho lưu trữ của tổ chức bạn rồi đưa cho Agent, nên đường dẫn bạn cung cấp không cần sống lâu sau đó.
Tính năng tải tệp đính kèm tắt cho tới khi quản trị viên bật nó. Khi tắt, và với mọi tệp vượt giới hạn hoặc tải thất bại, tệp bị bỏ qua nhưng
text vẫn tới được Agent. Vì vậy tin nhắn có đính kèm không bao giờ là lỗi, và bạn không thể biết từ mã phản hồi là tệp đã được nhận hay chưa.Dữ liệu bổ sung
Đối tượngmetadata của bạn tới Agent ở dạng lồng bên dưới client_metadata. Nó không bao giờ được trộn vào tầng trên cùng, vì các khóa ở đó, nhất là định danh người nhận, là thứ quyết định câu trả lời được gửi cho ai.
Các trường lạ ở tầng trên cùng của phần thân được bỏ qua, nên bạn có thể thêm trường riêng mà không sợ bị từ chối.
Luồng callback tin nhắn từ Agent
Câu trả lời của Agent được gửi tới webhook của bạn bằng một lệnh POST đã ký. Sự kiện gồm hai phần giống chiều gửi lên: nội dung sự kiện và chữ ký xác thực nội dung sự kiện.Kiểm chứng điểm cuối webhook
Trước khi tin nhắn thật đầu tiên tới, bạn có thể yêu cầu nền tảng chứng minh điểm cuối của bạn đúng là điểm cuối nó tưởng. Nút Kiểm tra callback trên Console gửi một yêu cầu đã ký:challenge:
- Yêu cầu kiểm chứng được ký bằng đúng cách ký như mọi gói tin khác, nên hàm xác thực bạn đã viết dùng được ngay.
- Trường
typechỉ xuất hiện trên yêu cầu kiểm chứng, không bao giờ có trên tin nhắn thật. - Bắt buộc phải lặp lại
challenge. Trả 200 suông là chưa đủ: một tên miền bỏ trống, một trang báo lỗi của CDN hay một bộ cân bằng tải đều trả về 200. - Mỗi lần kiểm chứng dùng một
challengekhác nhau. - Nền tảng không đi theo chuyển hướng. Mã 301, 302, 303 làm mất phần thân yêu cầu nên không bao giờ lặp lại đúng được.
- Không có gì được lưu lại. Kết quả đạt nghĩa là điểm cuối của bạn trả lời đúng tại thời điểm đó.
Cấu trúc sự kiện callback
Về cách đặt tên: Các trường ở tầng ngoài dùng kiểu lowerCamelCase (
eventId, integrationKey, conversationId, occurredAt), còn các khối nội dung và trường bên trong chúng dùng kiểu snake_case (quick_replies, sub_title, image_url, file_name). Đây là chủ ý, không phải nhầm lẫn: các khối nội dung dùng chung một bộ từ vựng với widget chat và với lịch sử hội thoại.Button
QuickReply
Carousel
Ảnh kèm nút bấm được gửi dưới dạng carousel có 1 item.
Reference
Attachment
Hiện chưa có tính năng nào của nền tảng đặt tệp lên lượt trả lời của kênh API, nên trường
attachments chưa bao giờ xuất hiện trên callback. Trường được giữ chỗ sẵn để tương thích về sau. Tệp đính kèm gửi lên thì hoạt động bình thường.Ví dụ nội dung sự kiện
Tin nhắn văn bản không có nútNhững điều cần lưu ý khi xử lý callback
textluôn có mặt, kể cả khi cóbuttonshaycarousels. Đó là phương án hiển thị dự phòng, nên một hệ thống chỉ hiển thị văn bản vẫn có một cuộc hội thoại đúng nghĩa.- Danh sách rỗng thì vắng mặt, không phải
[].buttons,quick_replies,carouselsvàreferencesbị lược bỏ hoàn toàn khi lượt trả lời không có. conversationIdvàfromcó thể vắng mặt trên callback đến từ phiên bản cũ của nền tảng. Hãy hiểu sự vắng mặt là chưa xác định.- Chống trùng theo
eventId, không phảirunId. - Hãy nối dữ liệu theo
conversationId, đừng nối theorunId, vìrunIdchỉ là một lượt trả lời. - Trả 200 ngay rồi xử lý bất đồng bộ. Mỗi lần gọi chỉ có 10 giây; xử lý chậm sẽ bị tính là thất bại và kích hoạt gửi lại.
Khi điểm cuối của bạn lỗi
Yêu cầu với Callback URL
- Giao thức
https(http chỉ dùng được trong môi trường phát triển nội bộ). - Không nhúng thông tin đăng nhập trong URL. Bạn xác thực nền tảng bằng chữ ký, không bằng bí mật giấu trên đường dẫn.
- Phải phân giải ra địa chỉ công khai trên Internet. Địa chỉ loopback, mạng nội bộ, link-local, unique-local, multicast và dải NAT của nhà mạng đều bị từ chối.
- Phép kiểm tra chạy ngay lúc kết nối, nên một tên miền phân giải ra địa chỉ nội bộ cũng bị từ chối.
- Tối đa 2048 ký tự.
Đọc lại lịch sử hội thoại
Đây là đường phục hồi cho lượt trả lời bạn không nhận được. Vì callback là cơ chế nhiều nhất một lần, sau 3 lần thất bại lượt trả lời nằm lại trong hàng đợi lỗi; nếu không có API này thì tin nhắn đó mất hẳn với bạn.
Phản hồi 200:
- Dùng khóa API của tổ chức, không dùng khóa bí mật của kênh. Khóa bí mật xác thực rằng máy chủ của bạn đang chuyển tiếp một tin nhắn; còn đây là bạn đọc dữ liệu của chính mình.
visitorTokenđọc được đúng phiên của chính nó; đọc phiên khác trả về 403SESSION_FORBIDDEN. - Phân trang bằng con trỏ, tin cũ nhất trước. Truyền
afterbằng mã tin nhắn cuối cùng bạn đang giữ;hasMorecho biết còn trang tiếp theo hay không. - Mã tin nhắn là chuỗi, vì chúng vượt ngưỡng số nguyên an toàn của JavaScript, nhưng vẫn sắp xếp theo thứ tự số.
- Hãy đọc
from, đừng đọcrole. Lượt trả lời của nhân viên được lưu với cùngrolenhư của Agent. Ở đâyfromcó ba giá trị:customer,bot,operator. - Giá trị
operatorhiện chưa phát sinh: nền tảng chưa có tính năng cho nhân viên trả lời thay Agent trên kênh này. Trường được giữ chỗ sẵn để tương thích về sau. - Mỗi dòng trả về là một lượt nói thật sự. Một dòng chỉ xuất hiện khi nó có
text, nút bấm, quick reply, carousel, thẻ trích dẫn hoặc tệp đính kèm, nên bạn không cần tự lọc bong bóng trống. aftersai định dạng trả về 400, không bị âm thầm bỏ qua.- Một mã 404 cho ba trường hợp: hội thoại không tồn tại, hội thoại của tổ chức khác, hoặc hội thoại nội bộ của nhân viên. Cố ý không phân biệt.
- Tệp đính kèm chỉ nêu tên, kiểu và kích thước, không kèm khóa đối tượng.
- Đọc lịch sử không tiêu tốn hạn mức lượt chạy. Nó có hạn mức riêng, mặc định 120 lần mỗi phút cho mỗi khóa API.
- Khi vượt hạn mức đọc lịch sử, phản hồi 429 chỉ kèm header
Retry-After.
Khóa API và hạn mức sử dụng
Tạo khóa API
Khóa API của tổ chức dùng cho API đọc lại lịch sử và API lấy đường dẫn tải tệp. Tạo tại trang API keys trên Console.- Khóa có dạng
sk-kèm 32 ký tự ngẫu nhiên, ví dụsk-9Kd2xQ…. - Khóa chỉ hiện đúng một lần, ngay khi tạo. Hệ thống chỉ lưu bản băm nên không có cách nào xem lại.
- Danh sách khóa hiển thị 12 ký tự đầu để bạn nhận ra khóa nào là khóa nào.
- Khóa hành động với quyền của người đã tạo ra nó, nên nó không cấp thêm quyền gì mới.
Thu hồi và xoay vòng khóa
Thu hồi là một thao tác đánh dấu, không phải xóa: mã khóa vẫn tra cứu được sau khi ngừng hoạt động, để dấu vết kiểm toán còn đọc được. Danh sách hiển thị cả khóa đã thu hồi. Xoay vòng khóa API là hai bước, theo đúng thứ tự: tạo khóa mới trước, thu hồi khóa cũ sau. Trong khoảng giữa, cả hai khóa đều dùng được.Xem hạn mức đã dùng
runslà ngân sách theo ngày UTC của cả tổ chức, dùng chung cho mọi hội thoại và mọi kênh. Nó về lại mức đầy vàoresetsAt, tức 00:00 UTC.rate.limitlà hạn mức theo phút cho mỗi bên gọi.- Xem hạn mức không tốn hạn mức, nên bạn có thể gọi thường xuyên.
- Chỉ khóa API gọi được.
visitorTokenbị từ chối với 403USAGE_FORBIDDEN.
Bước tiếp theo
Kênh Live Chat
Dùng khung chat dựng sẵn của nền tảng thay vì tự xây giao diện.
SDK di động
Đưa khung chat vào ứng dụng Android và iOS.
Phụ lục kỹ thuật
Bảng mã lỗi, bảng giới hạn hệ thống và danh mục kiểm tra trước Production.