Skip to main content
Phần này dành cho bên muốn dựng giao diện chat riêng thay vì dùng widget. Mọi đường dẫn dưới đây là tương đối so với https://agents.fpt.ai/direct-bff. Toàn bộ bề mặt này không dùng phiên đăng nhập của nền tảng. Khách hàng ẩn danh được cấp một visitorToken ngắn hạn, và chính token đó quyết định họ thuộc tổ chức nào, phiên nào, Agent nào.
Trình tự bắt buộc: đọc cấu hình → tạo phiên → mở luồng nhận trả lời → gửi tin nhắn.

1. Đọc cấu hình hiển thị

Không cần xác thực, vì giao diện phải vẽ được trước khi có phiên.
Phản hồi
  • turnstileSitekey, avatarUrl và localeDefault trả về null khi chưa đặt, không bao giờ là chuỗi rỗng. Một turnstileSitekey rỗng sẽ bật cổng chống bot mà không khách hàng nào qua được.
  • chat luôn là một đối tượng, không bao giờ null. Ba giá trị đúng/sai trong đó luôn có mặt và mặc định là bật.
  • chat.placeholder rỗng nghĩa là tổ chức không tự soạn; hãy dùng chuỗi đã dịch sẵn của bạn.
  • theme và welcome được trả về nguyên trạng, không kiểm tra định dạng. Hãy tự kiểm tra trước khi đưa vào CSS.
  • Danh sách tên miền cho phép không nằm trong kết quả và không chi phối lệnh này; nó được áp dụng ở lệnh tạo phiên.
Lệnh này trả về 404 CONNECTION_NOT_FOUND khi connectionKey không tồn tại, kênh chưa kết nối, chưa gắn Agent, hoặc kênh đã bị xóa. Lệnh không trả về 403 và không kiểm tra Origin, vì nội dung trả về là thông tin thương hiệu công khai. Cửa kiểm soát nằm ở lệnh tạo phiên.

2. Tạo phiên

Phản hồi 200
  • visitorToken sống 1 giờ. Gửi nó ở header Authorization: Bearer cho mọi lệnh gọi sau đó.
  • sessionId là định danh hội thoại, giữ nguyên qua các lần kết nối lại.
  • Tất cả định danh số nguyên đều là chuỗi trên đường truyền, vì chúng vượt ngưỡng số nguyên an toàn của JavaScript.

Khách hàng quay lại

Gửi kèm visitorToken cũ thì hệ thống nối lại đúng khách hàng và đúng hội thoại, kèm hạn dùng mới. Token vẫn được chấp nhận kể cả khi đã hết hạn: hạn dùng chỉ chặn việc sử dụng, còn chữ ký mới là căn cứ xác định danh tính. Token thiếu, hỏng, sai chữ ký hoặc thuộc tổ chức khác không gây lỗi: hệ thống lặng lẽ cấp một danh tính mới. Về visitorName: gửi visitorToken mà không kèm tên thì tên đã lưu được giữ nguyên; gửi một tên khác thì tên mới ghi đè. Phản hồi 200 không trả tên về, để token không trở thành công cụ tra cứu danh tính.
Tên được cắt còn 64 ký tự, và bị bỏ hoàn toàn nếu chứa ký tự điều khiển hay ký tự xuống dòng Unicode. Không trường hợp nào báo lỗi: một cái tên không dùng được chỉ làm mất lời chào, không làm hỏng cuộc trò chuyện.

3. Mở luồng nhận trả lời

Luồng SSE phát các sự kiện theo chuẩn AG-UI: RunStarted, TextMessageStart / TextMessageContent / TextMessageEnd, ToolCall*, StateSnapshot, RunFinished, RunError. Tin nhắn nhiều định dạng (nút bấm, quick reply, carousel) đến trong sự kiện CUSTOM mang tên ui_blocks; thẻ nguồn trích dẫn đến trong CUSTOM mang tên references.
Mở luồng trước khi gửi tin nhắn đầu tiên. Token đã phát đi không bao giờ được phát lại: nếu mất kết nối giữa chừng, hãy tải lại tin nhắn đã hoàn tất từ lịch sử hội thoại thay vì chờ phát lại.
Thẻ trích dẫn trên luồng SSE mang thêm trường index (số thứ tự [n] gắn với câu trả lời) và ghi các trường vắng thành null. Thẻ trong callback kênh API và trong API đọc lịch sử không có index, và trường vắng bị lược bỏ hẳn. Nếu dùng chung một hàm phân tích, hãy coi index là tùy chọn và coi khóa vắng mặt giống khóa mang giá trị null.

4. Gửi một tin nhắn

  • metadata là một đối tượng đóng: chỉ postback được đọc, mọi khóa khác bị bỏ qua.
  • workspaceId, connectionId, endUserId và userId không được nhận trong phần thân vì đều lấy từ token. Gửi kèm bất kỳ trường nào sẽ nhận 400 WORKSPACE_ID_NOT_ACCEPTED; giá trị null tường minh được coi như không gửi. Riêng sessionId vẫn hợp lệ và được đối chiếu với token.
  • Phần thân tối đa 256 KiB.
  • Tối đa 5 tệp đính kèm trên một lượt gửi; tệp thứ sáu bị từ chối bằng 400 INVALID_ARGUMENT. Hãy kiểm tra số lượng ở phía giao diện trước khi tải lên.
Câu trả lời không nằm trong phản hồi của lệnh này; nó chảy về qua luồng SSE đã mở ở bước trước.
Nếu tổ chức đã gỡ kênh trong lúc hội thoại đang mở, lệnh vẫn trả về 200 nhưng status có giá trị channel_unavailable, output rỗng, và không có câu trả lời nào về qua SSE nữa. Hãy dừng chờ và báo cho khách rằng kênh không còn khả dụng; tin nhắn họ vừa gửi vẫn được lưu trong lịch sử.

5. Tải tệp đính kèm lên

Phản hồi
  • Một tệp cho mỗi lần gọi, tối đa 30 MiB.
  • Phần mở rộng được chấp nhận: pdf, doc, docx, ppt, pptx, jpg, jpeg, png, gif, svg, webp, heic, jfif, xlsx, xls, csv.
  • Phần mở rộng và các byte đầu tệp đều được kiểm tra. Tệp thực thi đổi tên thành .png sẽ bị từ chối bằng 415.
  • Không nhận workspaceId, sessionId hay connectionId trong form; tất cả lấy từ token.

6. Tải tệp do Agent tạo ra

Khi Agent tạo ra một tệp, thứ đi kèm câu trả lời là khóa đối tượng, không phải đường dẫn tải. Đổi khóa đó lấy một đường dẫn ngắn hạn:
Phản hồi
  • disposition quyết định tệp được tải về hay mở ra. Dùng attachment cho mọi đường dẫn bạn định điều hướng tới; dùng inline (mặc định) cho đường dẫn đặt vào thẻ img hoặc tải ngầm. Tệp lưu dưới dạng text/html sẽ hiển thị như một trang web khi trình duyệt điều hướng tới, thay thế luôn khung chat của khách hàng.
  • Đường dẫn sống 300 giây và được cấp mới mỗi lần gọi. Đừng lưu lại, hãy xin lại khi cần.
  • visitorToken chỉ đổi được tệp do chính hội thoại của họ tạo ra. Khóa API đổi được mọi tệp trong tổ chức của mình.
  • 404 cho cả tệp không tồn tại lẫn tệp không thuộc quyền, cố ý dùng chung một mã để không ai dò được tệp nào đang tồn tại.

7. Đánh giá một câu trả lời

Trả về 204 không kèm nội dung. rating nhận up, down, hoặc none để thu hồi đánh giá. Thu hồi một đánh giá vốn không tồn tại cũng trả về 204.
  • messageId là mã tin nhắn AG-UI mà giao diện của bạn đã hiển thị, không phải mã số nội bộ.
  • comment là tùy chọn và bị cắt ở 2000 ký tự chứ không bị từ chối.
  • Không có đường đọc ngược: nếu muốn giữ trạng thái nút thích sau khi tải lại trang, hãy tự nhớ ở phía giao diện.

CORS và tên miền cho phép

Bắt buộc đọc trước khi gọi Visitor API từ giao diện của chính bạn.