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 URL | https://apic.schoolsai.work |
| Format | JSON (request & response) |
| Auth | Authorization: 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ường | Kiểu | Bắt buộc | Ghi chú |
| tenant | string | có | 3-40 ký tự: chữ thường/số/gạch ngang/gạch dưới |
| bot_name | string | có | 2-80 ký tự |
| greeting | string | không | tối đa 500 ký tự |
| system_prompt | string | không | tố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ường | Kiểu | Ghi chú |
| bot_name, bot_avatar, color, webhook, greeting, system_prompt, response_language, model, owner_telegram_chat_id, cloudinary_*, brand_logo_url | string | trim tự động |
| temperature | number | 0 – 2 |
| max_tokens | integer | 1 – 32768 |
| streaming | boolean | |
{ "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ường | Kiểu | Bắt buộc |
| messages | array<{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." }
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.
- 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.
- Bot sẽ trả về 1 Verify Token riêng cho đúng trang của bạn — copy lại.
- 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.
- 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ường | Kiểu | Bắt buộc | Ghi chú |
| session | string | có | |
| question | string | có | ≤ 10.000 ký tự |
| lesson_id | string | không | có 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ường | Kiểu | Bắt buộc | Ghi chú |
| customer_name | string | có | tên hiển thị trong chat |
| customer_context | object | không | ghi 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ường | Kiểu | Bắt buộc |
| session | string | có |
| context | object | có — 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).
| Query | Ghi chú |
| session | lọ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ường | Kiểu | Bắt buộc | Ghi chú |
| session | string | có | ≤ 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) |
| text | string | không* | ≤ 10.000 ký tự |
| media | array | khô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 form | Bắt buộc | Ghi chú |
| audio | có | File âm thanh, ví dụ audio/webm |
| session | có | Giữ nguyên giữa các lượt để AI workspace nhớ hội thoại |
| username | không | Tên hiển thị của người gọi |
| duration_ms | không | Thờ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.
| Query | Ghi chú |
|---|
| session | lọ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ường | Bắt buộc |
| session | có |
| cf_session_id, track_name | có — 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ường | Bắt buộc |
| call_id | có |
| cf_session_id, track_name | có |
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ường | Bắt buộc | Ghi chú |
| call_id | có | |
| reason | không | mặ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.
| Query | Ghi chú |
|---|
| status | lọ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ường | Kiểu | Bắt buộc |
| title, content | string | có |
| image_prompt, image_url, video_url | string | không |
| platforms | string[] | không — mặc định ["facebook"] |
| auto_approve | boolean | true = 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ường | Bắt buộc | Ghi chú |
| topic | có | |
| count | không | mặ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ường | Kiểu | Bắt buộc | Ghi chú |
| text | string | có | ≤ 500.000 ký tự |
| title | string | khô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ường | Kiểu | Bắt buộc | Ghi chú |
| lesson_id | string | có | 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á |
| title | string | không | |
| content | string | có | 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".
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ường | Kiểu | Bắt buộc | Ghi chú |
| name | string | có | chỉ chữ/số/gạch dưới |
| description | string | không | để model biết khi nào nên gọi tool này |
| method | string | không | mặc định GET — kênh marketplace-chat chỉ chạy được GET |
| url_template | string | có | vd https://api.client.com/search?q={keyword}&price_max={price_max} |
| parameters_schema | string | không | JSON Schema dạng chuỗi, mô tả tham số model được điền |
| headers_template | string | không | JSON dạng chuỗi, vd API key tĩnh của bạn |
| result_path | string | không | đường dẫn lấy phần cần thiết từ response JSON, vd data.results |
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ường | Kiểu | Bắt buộc | Ghi chú |
| session | string | có | |
| messages | array | có | có — tối đa 100 message, message cuối phải là "user" |
| item_id | string | không | khá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_context | string | khô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ường | Kiểu | Bắt buộc | Ghi chú |
| request_id | UUID | có | Duy nhất cho mỗi lượt; dùng làm idempotency key |
| session | string | có | Không dùng chung cho các request đồng thời |
| messages | array | có | 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.
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ường | Kiểu | Bắt buộc | Ghi chú |
| tenant | string | có | 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_name | string | có | 2-80 ký tự |
| greeting | string | không | |
| system_prompt | string | không | dù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ường | Kiểu | Bắt buộc | Ghi chú |
| content_type | string | có | "blog" · "social" |
| times | string[] | có | mảng "HH:MM", 1 giờ = 1 bài/ngày áp dụng |
| days | string[] | không | mon..sun; rỗng/bỏ qua = áp dụng hàng ngày |
| is_active | boolean | khô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).
| Method | Path | Ghi chú |
| GET | /loyalty/program | đọc cấu hình chương trình loyalty của tenant |
| PUT | /loyalty/program | tạo/cập nhật cấu hình chương trình |
| POST | /loyalty/sales | ghi 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/account | xem điểm/hạng của 1 khách |
| GET | /loyalty/reward-world/campaigns | danh sách campaign quay thưởng |
| POST | /loyalty/reward-world/campaigns/:id/join | khách tham gia campaign |
| POST | /loyalty/reward-world/spins | thực hiện 1 lượt quay |
| GET | /loyalty/reward-world/rewards | danh sách phần thưởng đã trúng |
| POST | /loyalty/reward-world/rewards/:id/claim | nhậ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).
| Method | Path |
| 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 |
| 400 | Thiếu field bắt buộc hoặc dữ liệu không hợp lệ — chi tiết trong error |
| 401 | Thiếu/sai Authorization: Bearer <api_key> |
| 403 | Record tồn tại nhưng thuộc tenant khác |
| 404 | Không tìm thấy resource (post/session/call/schedule id) |
| 409 | Xung đột — vd tenant slug đã dùng, hoặc dependency content-plan chưa sẵn sàng |
| 502 | Lỗ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é!"
}'