👁 Chỉ lưu trong trình duyệt của bạn (localStorage) — dùng cho mọi nút "Gửi request" trên trang này.

Knowledge Worker API

Tài liệu tham khảo cho toàn bộ API công khai (/api/v1/*) của Knowledge Worker — dùng khi hệ thống ngoài (POS, CRM, tổng đài...) muốn tích hợp chatbot, gọi thoại, loyalty và quản lý nội dung theo từng tenant. Mỗi endpoint chính có nút "Thử API" để gọi thật ngay trên trang, không cần Postman.

Base URL & Xác thực

Base URLhttps://apic.schoolsai.work
FormatJSON (request & response)
AuthAuthorization: Bearer <api_key>

Lấy API key ở đâu?

Mỗi tenant có 1 api_key riêng (sinh khi tạo bot qua /api/onboarding/register hoặc POST /api/v1/bots), xem lại trong trang cấu hình (config.html) của dashboard. Dán key vào ô "API key" ở đầu trang này để dùng các nút "Thử API" bên dưới.

Luôn truyền API key bằng header Authorization: Bearer sk_xxx. Không đặt secret trong URL hoặc query string.

Mọi endpoint dưới đây đều tự động giới hạn theo đúng tenant gắn với API key — không có cách nào đọc/ghi dữ liệu của tenant khác.

Bot & cấu hình

Tạo bot mới, đọc/ghi cấu hình, hoặc để một trợ lý AI tự cập nhật cấu hình qua hội thoại tự nhiên.

POST/api/v1/botscần API key

Tạo thêm 1 bot/tenant mới từ một tài khoản đã có API key.

TrườngKiểuBắt buộcGhi chú
tenantstringcó3-40 ký tự: chữ thường/số/gạch ngang/gạch dưới
bot_namestringcó2-80 ký tự
greetingstringkhôngtối đa 500 ký tự
system_promptstringkhôngtối đa 10.000 ký tự
{ "success": true, "tenant": "abc-shop", "bot_name": "AB Shop", "api_key": "sk_...", "created_from": "your-tenant" }

Lỗi 409 nếu tenant đã tồn tại.

GET/api/v1/configcần API key

Đọc cấu hình hiện tại của bot (tên, màu, webhook, system_prompt, model, temperature, Telegram chat id, Cloudinary, brand_logo_url...).

{ "success": true, "config": { "bot_name": "...", "greeting": "...", "system_prompt": "...", "model": "...", "temperature": 0.3, "max_tokens": 1000, "streaming": true, "webhook": "...", "owner_telegram_chat_id": "...", "..." : "..." } }
PATCH/api/v1/configcần API key

Cập nhật một phần cấu hình — chỉ gửi field muốn đổi.

TrườngKiểuGhi chú
bot_name, bot_avatar, color, webhook, greeting, system_prompt, response_language, model, owner_telegram_chat_id, cloudinary_*, brand_logo_urlstringtrim tự động
temperaturenumber0 – 2
max_tokensinteger1 – 32768
streamingboolean 
{ "success": true, "updated": ["greeting", "temperature"] }
POST/api/v1/agent-chatcần API key

Chat tự nhiên với trợ lý cấu hình — model tự gọi tool để lưu cấu hình, kết nối kênh (Facebook/Instagram/Zalo/WordPress/Sanity...), thêm agent tool, lên lịch đăng bài, tạo chat-link, v.v. thay vì phải gọi từng API bên dưới bằng tay.

TrườngKiểuBắt buộc
messagesarray<{role: "user"|"assistant", content: string}>có — tối đa 100 message, message cuối phải là "user"
{ "success": true, "reply": "Đã cập nhật lời chào." }
GET/api/v1/agent-chat/toolscần API key

Liệt kê toàn bộ tool mà /agent-chat có thể gọi (built-in + tool tùy chỉnh của tenant).

Kết nối Messenger / Instagram (chat riêng + tự động trả lời bình luận)

Làm theo đúng 4 bước dưới đây — dùng ngay ô "Thử API" bên dưới, không cần công cụ nào khác.
  1. Gửi tin nhắn cho trợ lý cấu hình (ô "Thử API" ngay dưới đây) để kết nối Page/tài khoản Instagram của bạn — điền đúng page_id và access_token của bạn vào chỗ ví dụ trước khi gửi. Nếu trang này đã kết nối để đăng bài trước đó, chỉ cần tạo lại access_token có th\xEAm quyền pages_messaging v\xE0 pages_manage_engagement rồi kết nối lại — kh\xF4ng cần l\xE0m g\xEC kh\xE1c.
  2. Bot sẽ trả về 1 Verify Token riêng cho đúng trang của bạn — copy lại.
  3. V\xE0o Meta App Dashboard của bạn → Webhooks → đăng k\xFD: Callback URL cố định https://apic.schoolsai.work/meta-webhook, d\xE1n Verify Token vừa nhận, subscribe field messages (chat ri\xEAng) v\xE0 feed (Facebook)/comments (Instagram) nếu muốn tự động trả lời b\xECnh luận.
  4. Nếu App của bạn chưa qua App Review cho pages_messaging/pages_manage_engagement, chỉ t\xE0i khoản admin/tester của App mới nhận được tin nhắn/b\xECnh luận thật khi test — muốn nhận từ kh\xE1ch h\xE0ng thật cần ho\xE0n tất App Review tr\xEAn Meta.

Chat & khách hàng

Chatbot cho khách cuối, tạo link chat riêng cho từng khách (dùng trong SMS/Zalo/email), gửi ngữ cảnh khách hàng, và gửi/đọc tin nhắn phiên chat — đây là nhóm endpoint hệ thống ngoài (POS...) hay dùng nhất.

POST/api/v1/chatcần API key

Gửi 1 tin nhắn của khách tới chatbot AI và nhận câu trả lời (tương đương endpoint /chat nội bộ nhưng tự ép đúng tenant theo API key).

TrườngKiểuBắt buộcGhi chú
sessionstringcó 
questionstringcó≤ 10.000 ký tự
lesson_idstringkhôngcó thì trả lời theo đúng phạm vi lesson đó (xem Bài học (Lessons)), bỏ trống = bot trợ lý chung
POST/api/v1/chat-linkcần API key

Sinh 1 link chat riêng, gắn sẵn tên khách + (tuỳ chọn) ngữ cảnh khách hàng — không cần tạo tenant hay ghi session trước, widget chat.html tự đọc từ URL. Dùng khi hệ thống ngoài (vd POS) muốn gửi link chat cho khách qua SMS/Zalo/email.

TrườngKiểuBắt buộcGhi chú
customer_namestringcótên hiển thị trong chat
customer_contextobjectkhôngghi luôn ngữ cảnh khách (đơn hàng, mã KH...) cho session mới tạo
{ "success": true, "session": "a1b2c3d4-...", "chat_url": "https://chat.schoolsai.work/chat.html?bot=your-tenant&session=a1b2c3d4-...&u=Nguy%E1%BB%85n%20V%C4%83n%20A" }
PUT/api/v1/customer-contextcần API key

Ghi/cập nhật ngữ cảnh khách hàng (đơn hàng, trạng thái, ghi chú...) cho 1 session đã tồn tại — để chatbot AI có sẵn dữ liệu thật khi trả lời khách, không cần khách tự khai lại.

TrườngKiểuBắt buộc
sessionstringcó
contextobjectcó — object phẳng, không phải mảng
{ "success": true, "session": "a1b2c3d4-..." }
GET/api/v1/messagescần API key

Lấy lịch sử tin nhắn của tenant (tối đa 100, mới nhất trước).

QueryGhi chú
sessionlọc theo 1 session cụ thể (không truyền = toàn bộ tenant)
{ "success": true, "messages": [
  { "id": "...", "session": "...", "username": "Khách", "text": "...", "is_bot": false, "needs_human": true, "escalation_resolved": false, "created": "..." }
] }
POST/api/v1/messagescần API key

Gửi tin nhắn chủ động tới 1 session đã tồn tại — dùng để hệ thống ngoài (POS, CRM...) chủ động báo tin cho khách qua đúng cuộc chat đang mở (vd "đơn hàng đã xong"). Tự động đánh dấu mọi tin nhắn needs_human đang chờ của session đó là escalation_resolved.

TrườngKiểuBắt buộcGhi chú
sessionstringcó≤ 200 ký tự — phải là session đã có ít nhất 1 tin nhắn (tạo qua /chat-link hoặc khách đã chat 1 lần)
textstringkhông*≤ 10.000 ký tự
mediaarraykhông*Tối đa 10 phần tử {type: image|video|audio|file, url: HTTPS, caption?, filename?}. Phải có ít nhất text hoặc media. Với session Facebook, Instagram, WhatsApp hoặc Zalo, server gửi media qua API của nền tảng.
curl -X POST https://apic.schoolsai.work/api/v1/messages \
  -H "Authorization: Bearer sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{"session":"a1b2c3d4-...","text":"Đơn hàng đã xong!","media":[{"type":"video","url":"https://cdn.example.com/demo.mp4","caption":"Video hướng dẫn"}]}'
// 200
{ "success": true, ... }
// 404 — session chưa từng có tin nhắn nào
{ "error": "Không tìm thấy phiên trò chuyện" }

Nếu chưa chắc session còn tồn tại, gọi GET /api/v1/messages?session=... trước để kiểm tra, hoặc tạo session bằng POST /api/v1/chat-link ngay từ đầu (server tự lưu customer_context nếu có).

Gọi thoại với AI workspace

Client ghi một lượt âm thanh rồi gửi đến SchoolsAI. Worker xác thực API key, ép workspace theo tenant của key, chạy STT → AI workspace → TTS và trả lại transcript, câu trả lời cùng audio. Đây là API hội thoại theo lượt, không phải WebRTC streaming.

POST/api/v1/ai-voice/greetingcần API key

Tạo câu chào đầu cuộc gọi theo tên và ngôn ngữ cấu hình của workspace.

curl -X POST https://apic.schoolsai.work/api/v1/ai-voice/greeting \
  -H "Authorization: Bearer sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{"lang":"vi"}'

# 200
{ "success": true, "text": "Xin chào...", "audioBase64": "..." }
POST/api/v1/ai-voice/turncần API key

Gửi một lượt nói của khách đến đúng AI workspace gắn với API key.

Trường formBắt buộcGhi chú
audiocóFile âm thanh, ví dụ audio/webm
sessioncóGiữ nguyên giữa các lượt để AI workspace nhớ hội thoại
usernamekhôngTên hiển thị của người gọi
duration_mskhôngThời lượng audio để tính quota voice
curl -X POST https://apic.schoolsai.work/api/v1/ai-voice/turn \
  -H "Authorization: Bearer sk_xxx" \
  -F "audio=@voice.webm;type=audio/webm" \
  -F "session=customer-call-123" \
  -F "username=Nguyen Van A" \
  -F "duration_ms=4200"

# 200
{ "success": true, "transcript": "...", "reply": "...", "audioBase64": "...", "needsHuman": false }

Không gửi tenant; server luôn lấy workspace từ API key. Khi needsHuman=true, client có thể chuyển sang luồng cuộc gọi nhân viên qua Cloudflare Realtime.

Gọi thoại (voice)

Quản lý cuộc gọi thoại admin ↔ khách qua Cloudflare Realtime (SFU). Trạng thái ringing/active/ended được đồng bộ realtime cho cả 2 phía qua PocketBase, giống cơ chế của messages.

GET/api/v1/callscần API key

Liệt kê cuộc gọi chưa kết thúc (status != "ended") của tenant.

QueryGhi chú
sessionlọc theo session
POST/api/v1/calls/startcần API key

Admin bắt đầu 1 cuộc gọi tới khách trong 1 session.

TrườngBắt buộc
sessioncó
cf_session_id, track_namecó — id phiên WebRTC từ Cloudflare Realtime
POST/api/v1/calls/acceptcần API key

Admin trả lời cuộc gọi đang ringing.

TrườngBắt buộc
call_idcó
cf_session_id, track_namecó
POST/api/v1/calls/decline  ·  POST/api/v1/calls/endcần API key

Từ chối (chưa nghe) hoặc kết thúc (đang gọi) 1 cuộc gọi.

TrườngBắt buộcGhi chú
call_idcó 
reasonkhôngmặc định "declined" / "hangup"

Nội dung & đăng bài

Tạo/duyệt bài viết social & blog, xem trạng thái pipeline đăng bài, và chạy thủ công các job nền (crawl RSS, publish, agent).

GET/api/v1/postscần API key

Liệt kê tối đa 50 bài gần nhất, kèm targets (kênh đăng + trạng thái) và media.

QueryGhi chú
statuslọc bài có ít nhất 1 target ở trạng thái này (pending/approved/scheduled/publishing/published/error)
POST/api/v1/postscần API key

Tạo bài viết mới, tự tạo target cho từng kênh đã kết nối (mặc định facebook nếu không truyền platforms).

TrườngKiểuBắt buộc
title, contentstringcó
image_prompt, image_url, video_urlstringkhông
platformsstring[]không — mặc định ["facebook"]
auto_approvebooleantrue = tạo target ở trạng thái "approved" luôn, bỏ qua bước duyệt
POST/api/v1/posts/:id/approvecần API key

Duyệt toàn bộ target đang "pending" của 1 bài. Bị chặn (409) nếu bài thuộc content-plan mà dependency chưa sẵn sàng.

POST/api/v1/content-clustercần API key

Lên kế hoạch + viết nền (async) 1 cụm nhiều bài blog SEO xoay quanh 1 chủ đề, có liên kết nội bộ giữa các bài.

TrườngBắt buộcGhi chú
topiccó 
countkhôngmặc định 5, tối đa 8
GET/api/v1/statuscần API key

Đếm số bài theo từng trạng thái pipeline (pending/approved/scheduled/publishing/published/error).

POST/api/v1/trigger/rss-crawl · /trigger/publish · /trigger/agentcần API key

Chạy thủ công (không đợi cron) job crawl RSS + viết nháp, job publish bài đã duyệt, hoặc 1 lượt AI agent vận hành cho tenant hiện tại.

Knowledge base

Tài liệu làm ngữ cảnh RAG cho chatbot (embed vào Vectorize).

GET/api/v1/knowledgecần API key

Liệt kê tối đa 100 tài liệu (id, title, char_count, created).

POST/api/v1/knowledgecần API key

Thêm 1 tài liệu mới, tự động embed cho tìm kiếm ngữ nghĩa (tương đương endpoint nội bộ /embed, ép đúng tenant).

TrườngKiểuBắt buộcGhi chú
textstringcó≤ 500.000 ký tự
titlestringkhông≤ 200 ký tự
DELETE/api/v1/knowledge/:idcần API key

Xoá 1 tài liệu theo id.

POST/api/v1/knowledge/synccần API key

Đồng bộ lại toàn bộ nguồn tài liệu đã cấu hình (vd Sanity/WordPress) cho tenant.

Bài học (Lessons)

Cho hệ thống LMS/khoá học (vd skillgo-app) tạo 1 bot AI riêng cho từng bài học + 1 bot trợ lý chung cho toàn bộ khoá, mà không cần tạo hạ tầng riêng cho từng bài học.

Kiến trúc: vì sao chỉ 2 endpoint là đủ?

Không tạo workspace/RAG riêng cho từng lesson (không chịu nổi khi tạo hàng nghìn lesson/ngày). Thay vào đó: bot theo lesson lookup thẳng nội dung theo lesson_id và nhét vào đúng câu hỏi đó (không qua tìm kiếm ngữ nghĩa, luôn chính xác 100% trong phạm vi bài học) — kích hoạt bằng cách gửi kèm lesson_id khi gọi POST /api/v1/chat. Bot trợ lý chung vẫn dùng đúng 1 workspace RAG có sẵn của tenant (như nhóm Knowledge base ở trên) — mỗi lesson mới chỉ là thêm 1 tài liệu vào workspace đó, không tạo gì mới; gọi /api/v1/chat không kèm lesson_id để dùng bot này.
POST/api/v1/lessonscần API key

Tạo mới hoặc cập nhật 1 lesson (upsert theo lesson_id). Tự động: (1) lưu nội dung để bot theo lesson lookup thẳng, (2) nhúng vào workspace RAG của tenant cho bot trợ lý chung, (3) nếu lesson đã tồn tại thì tự xoá bản nhúng cũ trước khi nhúng lại — gọi lại API này mỗi khi sửa nội dung lesson là đủ, không cần tự quản lý embedding.

TrườngKiểuBắt buộcGhi chú
lesson_idstringcóid lesson bên hệ thống của bạn — ≤ 100 ký tự, dùng lại đúng id này khi chat/xoá
titlestringkhông 
contentstringcótoàn bộ nội dung bài học (text) — ≤ 500.000 ký tự
DELETE/api/v1/lessons/:lesson_idcần API key

Xoá 1 lesson — dọn luôn bản nhúng RAG tương ứng khỏi workspace của tenant, để bot trợ lý chung không còn nhắc tới nội dung đã xoá.

Checklist tích hợp cho dev hệ thống ngoài

1) Ở chỗ code tạo/sửa lesson đã có sẵn trong hệ thống của bạn, gọi thêm POST /api/v1/lessons ngay sau khi lưu thành công — không cần màn hình quản lý riêng.
2) Ở chỗ xoá lesson, gọi thêm DELETE /api/v1/lessons/:lesson_id.
3) Ở màn chat trong 1 bài học, gửi kèm lesson_id khi gọi POST /api/v1/chat; ở màn trợ lý chung thì bỏ trống field này.
4) Nếu lesson được tạo hàng loạt (vd hàng nghìn/ngày), gọi POST /api/v1/lessons qua hàng đợi/job nền — đừng gọi đồng bộ ngay trong request tạo lesson của người dùng cuối, tránh làm chậm thao tác của họ.

Marketplace / API ngoài

Cho khách hàng cuối tra cứu/tư vấn bằng chat khi bạn đã có sẵn 1 platform + API riêng (vd sàn bất động sản, việc làm, hoặc bất kỳ ngành nào khác) — không cần Knowledge Worker tự lưu dữ liệu, chỉ cần đăng ký API search có sẵn của bạn làm 1 "tool" cho AI gọi.

Cơ chế: đăng ký tool 1 lần, dùng lại cho mọi ngành

Đăng ký API search thật của bạn qua POST /api/v1/agent-tools (URL, tham số, cách đọc kết quả). Khách hàng cuối chat qua POST /api/v1/marketplace-chat — AI tự gọi đúng tool đã đăng ký để trả lời, không bịa thông tin ngoài kết quả tool trả về. Đổi ngành/thêm ngành mới chỉ là đăng ký thêm tool khác, không phải sửa code.

V1: chỉ hỗ trợ tra cứu (GET), chưa hỗ trợ tạo/sửa dữ liệu qua chat

Vì endpoint /marketplace-chat mở cho khách hàng ẩn danh, tool có method khác GET bị chặn ở tầng code (không chỉ ở system prompt) khi gọi qua kênh này — tránh bị lợi dụng tạo/ghi dữ liệu hàng loạt qua chat. Đăng ký tool GET (search/đọc chi tiết) vẫn dùng bình thường.

Dùng dữ liệu của bạn trong chat thường (WooCommerce / MCP)

Chat thường (widget, trang chat, kênh mạng xã hội) tự tra dữ liệu thật của đúng tenant bằng các tool GET đã đăng ký (xem bên dưới) và tool tên mcp_server (MCP Streamable HTTP: url_template = URL /mcp, headers_template = {"Authorization":"Bearer ..."}). Với WooCommerce, vào dashboard → config → Custom Agent Tools → mẫu WooCommerce — Tư vấn sản phẩm rồi nhập địa chỉ shop: hệ thống tạo 3 tool dùng Store API công khai (/wp-json/wc/store/v1/products), không cần API key. Bot trả giá (tự chia theo currency_minor_unit), mô tả, ảnh, link; video chỉ có khi mô tả sản phẩm nhúng video. Mỗi tin khách tốn thêm 1–2 lượt gọi model. Chi tiết: docs/API.md, mục "Kết nối dữ liệu của bạn vào chat".
GET/api/v1/agent-toolscần API key

Liệt kê các tool (API ngoài) tenant đã đăng ký.

POST/api/v1/agent-toolscần API key

Đăng ký 1 API ngoài làm tool cho AI gọi. Dùng {tên_tham_số} trong url_template để chèn giá trị model chọn.

TrườngKiểuBắt buộcGhi chú
namestringcóchỉ chữ/số/gạch dưới
descriptionstringkhôngđể model biết khi nào nên gọi tool này
methodstringkhôngmặc định GET — kênh marketplace-chat chỉ chạy được GET
url_templatestringcóvd https://api.client.com/search?q={keyword}&price_max={price_max}
parameters_schemastringkhôngJSON Schema dạng chuỗi, mô tả tham số model được điền
headers_templatestringkhôngJSON dạng chuỗi, vd API key tĩnh của bạn
result_pathstringkhôngđường dẫn lấy phần cần thiết từ response JSON, vd data.results
DELETE/api/v1/agent-tools/:idcần API key

Gỡ 1 tool đã đăng ký.

POST/api/v1/marketplace-chatcần API key

Chat cho khách hàng cuối — AI tự gọi đúng tool GET đã đăng ký (xem POST /agent-tools) để trả lời, áp dụng cùng giới hạn lượt chat/tháng như POST /chat.

TrườngKiểuBắt buộcGhi chú
sessionstringcó 
messagesarraycócó — tối đa 100 message, message cuối phải là "user"
item_idstringkhôngkhách đang xem đúng 1 mục cụ thể (vd trang chi tiết 1 tin BĐS) — server tự gọi thẳng tool tên get_item_detail (nếu đã đăng ký) với tham số id, không chờ model quyết định, đảm bảo tư vấn đúng mục đang xem
item_contextstringkhông≤ 20.000 ký tự — nếu hệ thống gọi API này (backend của bạn) đã có sẵn nội dung mục đang xem, gửi thẳng text đã làm sạch qua đây thay vì dùng item_id; ưu tiên hơn item_id nếu cả 2 cùng được gửi, tránh phải gọi ngược lại API của bạn

Tư vấn theo đúng 1 mục đang xem (vd trang chi tiết 1 tin)

Khi khách đang ở trang chi tiết 1 tin/mục cụ thể — giống hệt trường hợp học viên đang mở 1 lesson (xem Bài học (Lessons)) — không cần "tìm kiếm" gì cả vì đã biết chính xác id. Đăng ký thêm 1 tool GET tên đúng get_item_detail (nhận tham số id) trỏ vào API xem chi tiết của bạn, rồi gửi kèm item_id khi gọi /marketplace-chat — server tự lấy đúng dữ liệu mục đó và tư vấn trong phạm vi đó, không bịa.

AI Agent nội bộ (orchestration-only)

SchoolsAI chỉ suy luận và trả answer hoặc một tool directive. Client đã xác thực chịu trách nhiệm kiểm tra phạm vi dữ liệu, thực thi tool và xin xác nhận trước mọi thao tác ghi. Endpoint này không gọi URL của tool.

Xác nhận và thực thi thuộc CameraAIWork

operator-chat không tự thực thi tool. Với tool ghi, CameraAIWork nhận directive, kiểm tra lại account/site/camera, hiển thị xác nhận cho người dùng rồi mới gọi hệ thống camera.
POST/api/v1/operator-chatcần API key

Chat cho người vận hành nội bộ theo contract orchestration-only. Response luôn echo nguyên vẹn request_id.

TrườngKiểuBắt buộcGhi chú
request_idUUIDcóDuy nhất cho mỗi lượt; dùng làm idempotency key
sessionstringcóKhông dùng chung cho các request đồng thời
messagesarraycóTối đa 20 message; content tối đa 8.000 ký tự

Response thành công: { "request_id", "type":"answer", "answer", "finish_reason":"stop" } hoặc { "request_id", "type":"tool", "tool_call_id", "name", "args" }. Gửi kết quả tool ở lượt tiếp theo với cùng request/session và giữ nguyên tool_call_id. Tối đa 6 vòng tool.

GET/api/v1/agent-tool-proposalscần API key

Liệt kê tối đa 50 đề xuất gần nhất, mới nhất trước. Lọc theo trạng thái bằng query status.

TrườngKiểuBắt buộcGhi chú
statusstringkhôngpending / confirmed / rejected — bỏ trống để lấy tất cả
POST/api/v1/agent-tool-proposals/:id/confirmcần API key

Thực thi thật đề xuất đang pending — gọi tool với đúng tham số model đã chọn lúc đề xuất, ghi lại kết quả.

Lỗi 409 nếu đề xuất không còn ở trạng thái pending (đã xác nhận/từ chối trước đó — không xác nhận/từ chối lại được).

POST/api/v1/agent-tool-proposals/:id/rejectcần API key

Huỷ 1 đề xuất đang pending — không có gì được thực thi.

Nền tảng nhiều tenant (Platform → Store)

Dành cho 1 nền tảng (marketplace, SaaS...) muốn tự cấp phát và quản lý nhiều bot con cho khách hàng/store của chính họ — vd 1 sàn bất động sản cấp 1 bot riêng cho từng agency. Mỗi store là 1 workspace tách biệt hoàn toàn về dữ liệu (knowledge base, agent tools riêng), nhưng dùng chung 1 tài khoản Schoolsai và 1 gói cước — không phải mở tài khoản/hợp đồng riêng cho từng store.

Kiến trúc 2 tầng

Tầng 1 — Platform: 1 tài khoản Schoolsai (đăng ký/được cấp lúc onboard). Workspace mặc định của tài khoản này (tạo lúc đăng ký) đóng vai bot chung của platform — tư vấn chung, marketing, FAQ toàn nền tảng. Tầng 2 — Store: mỗi khách hàng/agency/store bên trong platform là 1 workspace phụ, tạo qua POST /api/account/workspaces bên dưới — có tenant slug + api_key riêng, dữ liệu cô lập hoàn toàn với store khác, nhưng quota tin nhắn tính chung vào 1 pool của platform.

Auth khác với các endpoint khác trên trang này

2 endpoint dưới đây xác thực bằng token đăng nhập của tài khoản platform (PocketBase auth token), KHÔNG phải api_key của 1 bot cụ thể — nên ô "API key" ở đầu trang này không dùng được ở đây, không có nút "Thử API" tương tác, dùng curl. Lấy token: POST /api/collections/tenants/auth-with-password với {"identity": "email@platform.com", "password": "..."}; refresh khi hết hạn qua POST /api/collections/tenants/auth-refresh kèm token cũ. Giữ credential này ở backend của platform (không phải ở client/app), coi như 1 secret dùng để tự động hoá tạo store.
GET/api/account/workspacescần token account

Liệt kê toàn bộ store (workspace) của platform, kèm gói cước hiện tại và số lượng đã dùng/giới hạn.

curl https://apic.schoolsai.work/api/account/workspaces \
  -H "Authorization: Bearer <account_token>"
# -> { "success": true, "plan": "pro", "limit": 10, "used": 3,
#      "workspaces": ["platform-default", "agency-x", "agency-y"] }
POST/api/account/workspacescần token account

Tạo 1 store mới (workspace phụ) cho platform. Giới hạn 10 lần gọi/giờ để chống tạo tràn lan.

TrườngKiểuBắt buộcGhi chú
tenantstringcó3-40 ký tự, chữ thường/số/gạch ngang/gạch dưới — nên dùng slug ổn định theo id nội bộ của store, vd agency-1024
bot_namestringcó2-80 ký tự
greetingstringkhông 
system_promptstringkhôngdùng cho thông tin tĩnh, nhỏ của store (giới thiệu, chính sách, giờ mở cửa) — xem khối "Chọn đúng cách nạp dữ liệu" bên dưới
curl -X POST https://apic.schoolsai.work/api/account/workspaces \
  -H "Authorization: Bearer <account_token>" -H "Content-Type: application/json" \
  -d '{"tenant":"agency-1024","bot_name":"Agency 1024 Assistant"}'
# -> 201 { "success": true, "tenant": "agency-1024", "bot_name": "...",
#          "api_key": "sk_xxx", "plan": "pro", "used": 4, "limit": 10 }
# LƯU NGAY "api_key" trả về — đây là api_key RIÊNG của store này, dùng cho
# mọi endpoint khác trên trang (agent-tools, marketplace-chat, config, knowledge...).

Lỗi 409 nếu tenant đã tồn tại (chọn slug khác). Lỗi 403 nếu platform đã đạt giới hạn số store của gói hiện tại (mặc định free 3 / pro 10) — liên hệ Schoolsai để nâng nếu platform cần nhiều store hơn, đây là giới hạn có thể nới riêng theo từng platform, không cố định.

DELETE/api/account/workspaces/:tenantcần token account

Xoá 1 store (workspace phụ) — xoá luôn bot và toàn bộ dữ liệu riêng của store đó (knowledge base, agent tools...), giải phóng 1 chỗ trong giới hạn số workspace. Không thể xoá workspace mặc định của tài khoản qua endpoint này.

curl -X DELETE https://apic.schoolsai.work/api/account/workspaces/agency-1024 \
  -H "Authorization: Bearer <account_token>"
# -> 200 { "success": true, "tenant": "agency-1024" }

Lỗi 404 nếu tenant không tồn tại hoặc không thuộc tài khoản của bạn. Lỗi 403 nếu cố xoá workspace mặc định của tài khoản (workspace gắn liền với chính account, không phải store tự tạo). Hành động này không thể hoàn tác — api_key của store đó ngừng hoạt động ngay lập tức.

Chọn đúng cách nạp dữ liệu cho mỗi store

Không phải cứ nhiều store là phải dùng Knowledge base (RAG/embedding). Chọn theo tính chất dữ liệu của từng store: (1) Thông tin tĩnh, nhỏ (giới thiệu, FAQ, chính sách, giờ mở cửa) — nhét thẳng vào system_prompt lúc tạo workspace hoặc qua PATCH /config sau đó, không cần gì thêm. (2) Dữ liệu động/hay đổi (danh sách tin đăng, catalog sản phẩm, lịch sử đơn hàng) — đăng ký tool GET trỏ vào API thật của store, để model tự gọi khi cần — không nhét vào prompt (sẽ tràn context khi data lớn dần) và cũng không cần embedding (dữ liệu luôn mới, không phải đồng bộ lại). (3) Tài liệu dài, ít đổi, không có API sẵn (hướng dẫn sử dụng, chính sách chi tiết) — mới dùng đến Knowledge base thật sự.

Model có thể từ chối gọi tool khi đối tượng được hỏi trùng thực thể nó "biết sẵn" — không liên quan cách viết mô tả

Model đôi khi từ chối gọi 1 tool đã đăng ký đúng khi giá trị tham số trùng với 1 đối tượng nó có sẵn kiến thức nền rộng (thương hiệu lớn, dự án nổi tiếng...) — model tự áp phản xạ "tôi không có quyền truy cập dữ liệu real-time của X" thay vì tin tưởng gọi tool, dù tool chạy bình thường và trả đúng dữ liệu nếu được gọi. Đã kiểm chứng kỹ, KHÔNG khắc phục được bằng: đổi wording của description, đổi tên tham số, đổi loại dữ liệu hỏi (số liệu vs mô tả), ra lệnh mạnh trong system prompt ("luôn gọi tool, không dùng kiến thức có sẵn"), lẫn đổi model trong cùng họ OpenAI (đã test gpt-4o-mini, gpt-4o, gpt-4.1 — cả 3 từ chối giống hệt nhau với cùng câu hỏi). Đây là hành vi alignment ở tầng huấn luyện của toàn bộ họ model GPT khi câu hỏi thuộc dạng "số liệu công khai của 1 tài nguyên internet có thể kiểm chứng" (vd lượt sao GitHub) — không phải bug hệ thống, và không sửa được từ phía cấu hình/prompt/model OpenAI. Chỉ ảnh hưởng nhóm câu hỏi kiểu này; dữ liệu nghiệp vụ nội bộ của store (tin đăng, catalog, hồ sơ khách...) không gặp vấn đề tương tự. Muốn loại bỏ hoàn toàn rủi ro này cần đổi sang nhà cung cấp model khác ngoài họ GPT (vd Gemini) — đây là thay đổi lớn hơn (khác định dạng API tool-calling), chưa được tích hợp cho kênh này.

Checklist tích hợp cho platform

1) Backend của platform giữ 1 cặp email/password Schoolsai (được cấp lúc onboard), login 1 lần lấy token, tự refresh khi hết hạn.
2) Khi có store/khách hàng mới đăng ký trên platform: gọi POST /api/account/workspaces, lưu api_key trả về vào DB của platform, khoá theo id store nội bộ.
3) Với mỗi store: nạp dữ liệu theo đúng loại (xem khối phía trên), dùng api_key riêng của store đó cho mọi API tiếp theo.
4) Nhúng widget /chat hoặc gọi /marketplace-chat bằng đúng api_key của store cho khách hàng cuối của store đó — không dùng chung 1 api_key cho nhiều store.
5) Theo dõi used/limit qua GET /api/account/workspaces định kỳ — quota tin nhắn tính chung cho cả platform, không tách theo từng store.

Lịch đăng tự động

Đặt luật lên lịch đăng bài tự động theo ngày trong tuần + khung giờ, cho nội dung blog hoặc social.

GET/api/v1/schedulescần API key
POST/api/v1/schedulescần API key
TrườngKiểuBắt buộcGhi chú
content_typestringcó"blog" · "social"
timesstring[]cómảng "HH:MM", 1 giờ = 1 bài/ngày áp dụng
daysstring[]khôngmon..sun; rỗng/bỏ qua = áp dụng hàng ngày
is_activebooleankhông 
PATCH/api/v1/schedules/:idcần API key

Cập nhật 1 phần (cùng field như tạo mới, tất cả optional).

DELETE/api/v1/schedules/:idcần API key

Loyalty /api/v1/loyalty/*

Chương trình khách hàng thân thiết, tích/đổi điểm, campaign quay thưởng (reward-world).

MethodPathGhi chú
GET/loyalty/programđọc cấu hình chương trình loyalty của tenant
PUT/loyalty/programtạo/cập nhật cấu hình chương trình
POST/loyalty/salesghi nhận 1 giao dịch bán hàng để tích điểm
POST/loyalty/redemptionsđổi điểm lấy ưu đãi
GET/loyalty/accountxem điểm/hạng của 1 khách
GET/loyalty/reward-world/campaignsdanh sách campaign quay thưởng
POST/loyalty/reward-world/campaigns/:id/joinkhách tham gia campaign
POST/loyalty/reward-world/spinsthực hiện 1 lượt quay
GET/loyalty/reward-world/rewardsdanh sách phần thưởng đã trúng
POST/loyalty/reward-world/rewards/:id/claimnhận thưởng (kích hoạt fulfillment provider)

Chưa có nút "Thử API" cho nhóm này trên trang — payload phức tạp/khác nhau theo campaign. Gọi trực tiếp bằng curl/Postman theo path ở trên (vẫn dùng cùng Authorization: Bearer <api_key>).

Content Planning /api/v1/content-planning/*

Lập kế hoạch nội dung dài hạn theo chủ đề, phân tích xu hướng, sinh bài + dịch, luồng duyệt/từ chối riêng cho content plan (khác với duyệt bài đơn lẻ ở /posts/:id/approve).

MethodPath
GET/content-planning/plans
POST/content-planning/plans
PATCH/content-planning/plans/:id
GET/content-planning/review
GET/content-planning/analytics/insights
POST/content-planning/analytics/import
POST/content-planning/trends/import
POST/content-planning/trends/recommend
POST/content-planning/plans/:id/review
POST/content-planning/plans/:id/approve-to-plan
POST/content-planning/plans/:id/topics/:topicId/schedule
POST/content-planning/items/:id/generate
POST/content-planning/items/:id/translate
POST/content-planning/items/:id/approve
POST/content-planning/items/:id/reject

Nhóm này chủ yếu phục vụ dashboard nội bộ (composer.html) — hệ thống ngoài thường chỉ cần nhóm "Chat & khách hàng" và "Loyalty" ở trên. Chưa có nút "Thử API" cho nhóm này.

Mã lỗi chung

StatusÝ nghĩa
400Thiếu field bắt buộc hoặc dữ liệu không hợp lệ — chi tiết trong error
401Thiếu/sai Authorization: Bearer <api_key>
403Record tồn tại nhưng thuộc tenant khác
404Không tìm thấy resource (post/session/call/schedule id)
409Xung đột — vd tenant slug đã dùng, hoặc dependency content-plan chưa sẵn sàng
502Lỗi khi gọi tới PocketBase/dịch vụ backend nội bộ

Mọi lỗi trả JSON dạng { "error": "..." }.

Ví dụ: chủ động báo tin cho khách khi đơn hàng xong

Flow điển hình cho hệ thống POS: tạo link chat gắn tên khách khi có đơn mới, lưu lại session; khi đơn chuyển trạng thái "Đã xong", gọi /messages để gửi thông báo vào đúng cuộc chat đó. Có thể thử trực tiếp bằng 2 nút "Thử API" ở mục /chat-link và /messages phía trên (nhớ copy đúng session trả về từ bước 1 sang bước 2).

# 1. Khi tạo đơn — sinh link chat riêng cho khách, kèm ngữ cảnh đơn hàng
curl -X POST https://apic.schoolsai.work/api/v1/chat-link \
  -H "Authorization: Bearer sk_xxx" -H "Content-Type: application/json" \
  -d '{
    "customer_name": "Nguyễn Văn A",
    "customer_context": { "order_id": "DH-1024", "status": "Đang xử lý" }
  }'
# -> { "session": "a1b2c3d4-...", "chat_url": "https://chat.schoolsai.work/chat.html?..." }
# Lưu lại session + gửi chat_url cho khách qua SMS/Zalo/email.

# 2. Khi đơn chuyển "Đã xong" — gửi tin nhắn chủ động vào đúng session đã lưu
curl -X POST https://apic.schoolsai.work/api/v1/messages \
  -H "Authorization: Bearer sk_xxx" -H "Content-Type: application/json" \
  -d '{
    "session": "a1b2c3d4-...",
    "text": "Đơn hàng DH-1024 của bạn đã xong, mời bạn ghé lấy nhé!"
  }'