Skip to main content
Kênh API dành cho bên đã có sẵn kênh trò chuyện với khách hàng: ứng dụng riêng, Zalo OA tự vận hành, tổng đài, CRM. Hệ thống của bạn chuyển tiếp tin nhắn của khách tới nền tảng qua webhook; câu trả lời của Agent được gửi ngược lại webhook của bạn bằng callback.
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.
Bảng cấu hình kênh API
Khóa ký chỉ hiển thị đúng một lần, ngay tại thời điểm tạo. Hệ thống chỉ lưu bản băm nên không có cách nào xem lại. Hãy sao chép và cất vào kho bí mật của bạn 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ị.
Đừng bỏ qua bước cuối. Khóa cũ vẫn dùng được cho tới khi bạn kết thúc vòng đổi khóa, nên nếu quên, bạn đã không thực sự đổi khóa.

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 header X-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ý:
Hầu hết lỗi chữ ký đến từ việc framework web đã tự chuyển phần thân thành đối tượng trước khi bạn kịp đọc. Hãy cấu hình để giữ lại chuỗi byte nguyên bản (express.raw, bodyParser.raw, hoặc đọc trực tiếp từ luồng đầu vào) rồi mới phân tích JSON sau khi đã xác thực xong.

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ố

text luôn bắt buộc, kể cả với tin nhắn chỉ có postback. Lượt nói của khách hàng được lưu lại và hiển thị cho nhân viên khi họ tiếp quản hội thoại, nên một postback không kèm chữ sẽ để lại một bong bóng trống trong hộp thư của họ. Hãy gửi nhãn của nút làm text.
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.
Hãy dùng mã tin nhắn ổn định của chính bạn. Đừng sinh mã mới cho mỗi lần thử lại, làm vậy là tự vô hiệu hóa cơ chế chống trùng.

Tệp đính kèm gửi lên

Mỗi mục trong attachments 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ượng metadata 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ý:
Để vượt qua, hãy trả về mã 2xx kèm một phần thân JSON lặp lại đúng giá trị 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 type chỉ 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 challenge khá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

Ả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út
Tin nhắn có nút
Quick reply
Carousel
Câu trả lời có trích dẫn nguồn

Những điều cần lưu ý khi xử lý callback

  • text luôn có mặt, kể cả khi có buttons hay carousels. Đó 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, carousels và references bị lược bỏ hoàn toàn khi lượt trả lời không có.
  • conversationId và from có 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ải runId.
  • Hãy nối dữ liệu theo conversationId, đừng nối theo runId, vì runId chỉ 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

Khi điểm cuối của bạn gián đoạn kéo dài, cơ chế giao nhận là nhiều nhất một lần. Đây là đánh đổi có chủ ý: gửi lại vô hạn sẽ làm nghẽn bộ phận giao nhận dùng chung. Đường phục hồi là API đọc lại lịch sử ở mục dưới; hàng đợi lỗi nằm ở phía nền tảng và không tự động phát lại cho bạn.

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ề 403 SESSION_FORBIDDEN.
  • Phân trang bằng con trỏ, tin cũ nhất trước. Truyền after bằng mã tin nhắn cuối cùng bạn đang giữ; hasMore cho 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 đọc role. Lượt trả lời của nhân viên được lưu với cùng role như của Agent. Ở đây from có ba giá trị: customer, bot, operator.
  • Giá trị operator hiệ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.
  • after sai đị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.
Đây là đường phục hồi, không phải kho lưu trữ. Khi một khách hàng được xóa theo chính sách lưu giữ dữ liệu, toàn bộ tin nhắn của họ bị xóa hẳn. Hãy đồng bộ những gì bạn cần giữ về hệ thống của mình.

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.
Thu hồi có độ trễ tới 30 giây, vì khóa được lưu đệm. Kết quả trả về trường revocationDelaySeconds nói rõ con số này. Nếu một khóa bị lộ, hãy thu hồi ngay và tính tới khoảng trễ đó.
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

  • runs là 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ào resetsAt, tức 00:00 UTC.
  • rate.limit là 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. visitorToken bị từ chối với 403 USAGE_FORBIDDEN.
Các API có tính vào hạn mức còn gắn thêm ba header trên cả phản hồi thành công lẫn phản hồi 429:

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.