Service: HPCN AI Agent Service
Base URL: /ai-service/api
Authentication: Traefik forward-auth headers (X-Auth-Email, X-Auth-User-ID, X-Auth-Work-Unit)
Knowledge Base Management API cho phép quản lý danh mục (categories), cột dữ liệu (columns), và bản ghi (records) của Knowledge Base. Dữ liệu được lưu trữ trong MongoDB với hỗ trợ multi-tenant.
Hierarchy:
KBCategory (Danh mục: DM_XXX)
├── KBColumnDef (Cột: name, data_type, source_config)
└── KBDataRecord (Bản ghi: DL_N, dynamic data)
Tất cả requests đều yêu cầu Traefik forward-auth headers:
X-Auth-Email: user@company.com
X-Auth-User-ID: <uuid>
X-Auth-Work-Unit: <unit>
X-Tenant-Id: 2122030000
Tenant ID được trích xuất tự động từ headers. Không cần truyền vào URL hay body.
{
"success": true,
"data": [
{ "field1": "value1", ... }
],
"length": 1,
"total_record": 100
}
{
"success": false,
"data": [],
"length": 0,
"error": "Lỗi mô tả chi tiết",
"error_code": 7000000
}
| Code | Message | Cause |
|---|---|---|
| MSG_0001 | Tên danh mục/cột/bản ghi không được để trống | Empty name field |
| MSG_0002 | Danh mục đã tồn tại | Duplicate category name in tenant |
| MSG_0007 | Cột dữ liệu đã tồn tại | Duplicate column name in category |
| MSG_0014 | Không thể xóa danh mục. Danh mục hiện đang được sử dụng bởi: {names} | Category is referenced by other columns |
| MSG_0015 | Không thể xóa cột này. Cột dữ liệu hiện đang được sử dụng bởi: {names} | Column is referenced by other columns |
GET /kb/categories
Description: Lấy danh sách tất cả danh mục của tenant, kèm statistics.
Query Parameters:
| Name | Type | Default | Description |
|---|---|---|---|
| page | integer | 1 | Trang thứ mấy (1-indexed) |
| page_size | integer | 20 | Số record/trang (1-100) |
| sort_by | string | created_at | Sắp xếp theo: name, created_at, updated_at, category_code |
| sort_order | string | desc | asc | desc |
| keyword | string | null | Tìm kiếm trong name, description (contains, case-insensitive) |
Response:
{
"success": true,
"data": [
{
"category_id": "507f1f77bcf86cd799439011",
"category_code": "DM_LOAI_DICH_VU",
"name": "Loại dịch vụ",
"description": "Danh mục các loại dịch vụ",
"so_cot": 5,
"so_active": 15,
"so_pending": 3,
"tong_ban_ghi": 18,
"created_at": "2026-06-15T10:30:00+00:00",
"updated_by": "user@company.com"
}
],
"length": 1,
"total_record": 15
}
Example:
curl -X GET "https://api.example.com/ai-service/api/kb/categories?page=1&page_size=20&keyword=loai" \
-H "X-Auth-Email: user@company.com" \
-H "X-Auth-User-ID: 550e8400-e29b-41d4-a716-446655440000" \
-H "X-Tenant-Id: 2122030000"
POST /kb/categories
Description: Thêm mới danh mục. Mã danh mục (category_code) được auto-generate từ tên.
Request Body:
{
"name": "Loại dịch vụ",
"description": "Danh mục các loại dịch vụ (tùy chọn)"
}
Response:
{
"success": true,
"data": [
{
"category_id": "507f1f77bcf86cd799439011",
"category_code": "DM_LOAI_DICH_VU",
"name": "Loại dịch vụ"
}
],
"length": 1
}
Validation Rules:
name: bắt buộc, không rỗng, unique per tenantdescription: tùy chọnExample:
curl -X POST "https://api.example.com/ai-service/api/kb/categories" \
-H "Content-Type: application/json" \
-H "X-Auth-Email: user@company.com" \
-d '{
"name": "Loại dịch vụ",
"description": "Danh mục các loại dịch vụ"
}'
PUT /kb/categories/{category_id}
Description: Sửa thông tin danh mục.
Path Parameters:
| Name | Type | Description |
|---|---|---|
| category_id | string | MongoDB ObjectId của danh mục |
Request Body:
{
"name": "Tên danh mục mới (tùy chọn)",
"description": "Mô tả danh mục mới (tùy chọn)"
}
Response:
{
"success": true,
"data": [
{
"category_id": "507f1f77bcf86cd799439011",
"name": "Tên danh mục mới"
}
],
"length": 1
}
Example:
curl -X PUT "https://api.example.com/ai-service/api/kb/categories/507f1f77bcf86cd799439011" \
-H "Content-Type: application/json" \
-H "X-Auth-Email: user@company.com" \
-d '{
"name": "Loại dịch vụ (cập nhật)",
"description": "Danh mục các loại dịch vụ - bản cập nhật"
}'
GET /kb/categories/{category_id}/references
Description: Kiểm tra danh mục có đang được tham chiếu bởi cột khác không. Dùng trước khi xóa.
Response:
{
"success": true,
"data": [
{
"type": "column",
"column_id": "507f191e810c19729de860ea",
"column_name": "Dropdown - Dịch vụ",
"category_id": "507f1f77bcf86cd799439011"
}
],
"length": 1,
"is_referenced": true
}
Example:
curl -X GET "https://api.example.com/ai-service/api/kb/categories/507f1f77bcf86cd799439011/references" \
-H "X-Auth-Email: user@company.com"
DELETE /kb/categories/{category_id}
Description: Xóa danh mục. Chỉ được phép nếu danh mục không được tham chiếu bởi bất kỳ cột nào. Xóa cascade: toàn bộ columns và records của danh mục.
Response:
{
"success": true,
"data": [
{
"category_id": "507f1f77bcf86cd799439011",
"deleted": true
}
],
"length": 1
}
Error Response (nếu được tham chiếu):
{
"success": false,
"data": [],
"length": 0,
"error": "Không thể xóa danh mục. Danh mục hiện đang được sử dụng bởi: Dropdown - Dịch vụ"
}
Example:
curl -X DELETE "https://api.example.com/ai-service/api/kb/categories/507f1f77bcf86cd799439011" \
-H "X-Auth-Email: user@company.com"
GET /kb/categories/{category_id}/columns
Description: Lấy danh sách cột của một danh mục.
Response:
{
"success": true,
"data": [
{
"column_id": "507f191e810c19729de860ea",
"name": "Tên dịch vụ",
"data_type": "textbox",
"required": true,
"description": "Tên của dịch vụ",
"order": 1,
"source_config": null,
"updated_at": "2026-06-15T10:30:00+00:00",
"updated_by": "user@company.com"
},
{
"column_id": "507f191e810c19729de860eb",
"name": "Loại dịch vụ",
"data_type": "dropdown",
"required": false,
"description": "Phân loại dịch vụ",
"order": 2,
"source_config": {
"type": "data_source",
"category_id": "507f1f77bcf86cd799439012",
"display_column_id": "507f191e810c19729de860ec"
}
}
],
"length": 2
}
POST /kb/categories/{category_id}/columns
Description: Thêm mới cột vào danh mục.
Request Body:
{
"name": "Tên cột",
"data_type": "textbox",
"required": false,
"description": "Mô tả cột (tùy chọn)",
"order": 1,
"source_config": null
}
Parameters:
| Field | Type | Required | Options | Description |
|---|---|---|---|---|
| name | string | ✓ | — | Tên cột, unique per category |
| data_type | string | ✓ | textbox, textarea, number, dropdown, multi_select, email | Kiểu dữ liệu |
| required | boolean | ✗ | true/false | Default: false |
| description | string | ✗ | — | Mô tả cột |
| order | integer | ✗ | — | Thứ tự hiển thị (default: 0) |
| source_config | object | ✗ | — | Cấu hình datasource cho dropdown/multi_select |
source_config Schema:
Type 1: Dropdown/Multi_select từ danh mục khác (data_source)
{
"type": "data_source",
"category_id": "507f1f77bcf86cd799439012",
"display_column_id": "507f191e810c19729de860ec"
}
category_id: ID của danh mục chứa dữ liệudisplay_column_id: ID của cột được hiển thị (phải là loại textbox/textarea/email)Type 2: Dropdown/Multi_select manual (hardcoded)
{
"type": "manual",
"values": ["Option 1", "Option 2", "Option 3"]
}
Response:
{
"success": true,
"data": [
{
"column_id": "507f191e810c19729de860ea",
"name": "Tên dịch vụ",
"data_type": "textbox"
}
],
"length": 1
}
Examples:
Textbox đơn giản:
curl -X POST "https://api.example.com/ai-service/api/kb/categories/507f1f77bcf86cd799439011/columns" \
-H "Content-Type: application/json" \
-H "X-Auth-Email: user@company.com" \
-d '{
"name": "Tên dịch vụ",
"data_type": "textbox",
"required": true,
"order": 1
}'
Dropdown từ danh mục khác:
curl -X POST "https://api.example.com/ai-service/api/kb/categories/507f1f77bcf86cd799439011/columns" \
-H "Content-Type: application/json" \
-H "X-Auth-Email: user@company.com" \
-d '{
"name": "Loại dịch vụ",
"data_type": "dropdown",
"source_config": {
"type": "data_source",
"category_id": "507f1f77bcf86cd799439012",
"display_column_id": "507f191e810c19729de860ec"
},
"order": 2
}'
Dropdown manual:
curl -X POST "https://api.example.com/ai-service/api/kb/categories/507f1f77bcf86cd799439011/columns" \
-H "Content-Type: application/json" \
-H "X-Auth-Email: user@company.com" \
-d '{
"name": "Mức độ ưu tiên",
"data_type": "dropdown",
"source_config": {
"type": "manual",
"values": ["Thấp", "Trung bình", "Cao", "Cấp bách"]
},
"order": 3
}'
PUT /kb/categories/{category_id}/columns/{column_id}
Description: Sửa thông tin cột.
Request Body:
{
"name": "Tên cột mới (tùy chọn)",
"data_type": "textbox",
"required": true,
"description": "Mô tả mới",
"order": 2,
"source_config": null
}
Response:
{
"success": true,
"data": [
{
"column_id": "507f191e810c19729de860ea",
"name": "Tên cột mới"
}
],
"length": 1
}
GET /kb/categories/{category_id}/columns/{column_id}/references
Description: Kiểm tra cột có đang được tham chiếu bởi cột khác không (trong source_config).
Response:
{
"success": true,
"data": [
{
"type": "column",
"column_id": "507f191e810c19729de860eb",
"column_name": "Loại dịch vụ (dropdown)",
"category_id": "507f1f77bcf86cd799439011"
}
],
"length": 1,
"is_referenced": true
}
DELETE /kb/categories/{category_id}/columns/{column_id}
Description: Xóa cột. Chỉ được phép nếu không được tham chiếu bởi cột khác.
Response:
{
"success": true,
"data": [
{
"column_id": "507f191e810c19729de860ea",
"deleted": true
}
],
"length": 1
}
POST /kb/categories/{category_id}/columns/{column_id}/clone
Description: Nhân bản cột. Tên mới = tên cũ + "_copy" (nếu trùng thêm số).
Response:
{
"success": true,
"data": [
{
"column_id": "507f191e810c19729de860ed",
"name": "Tên dịch vụ_copy"
}
],
"length": 1
}
GET /kb/categories/{category_id}/records
Description: Lấy danh sách bản ghi của một danh mục, hỗ trợ filter, search, sort.
Query Parameters:
| Name | Type | Default | Description |
|---|---|---|---|
| page | integer | 1 | Trang thứ mấy |
| page_size | integer | 20 | Số record/trang |
| sort_by | string | created_at | Sắp xếp theo: created_at, updated_at, record_code |
| sort_order | string | desc | asc | desc |
| status | string | null | pending | active |
| updated_by | string | null | Filter theo user update (email hoặc "system") |
| created_from | date | null | Từ ngày (YYYY-MM-DD) |
| created_to | date | null | Đến ngày |
| updated_from | date | null | Từ ngày cập nhật |
| updated_to | date | null | Đến ngày cập nhật |
| keyword | string | null | Tìm kiếm full-text trên tất cả fields trong data dict |
Response:
{
"success": true,
"data": [
{
"record_id": "507f191e810c19729de860ee",
"record_code": "DL_1",
"data": {
"Tên dịch vụ": "Tư vấn kỹ thuật",
"Loại dịch vụ": "Dịch vụ hỗ trợ",
"Mức độ ưu tiên": "Cao"
},
"business_status": "active",
"created_at": "2026-06-15T10:30:00+00:00",
"updated_at": "2026-06-16T14:20:00+00:00",
"updated_by": "user@company.com"
}
],
"length": 1,
"total_record": 50
}
Example:
curl -X GET "https://api.example.com/ai-service/api/kb/categories/507f1f77bcf86cd799439011/records?page=1&status=active&keyword=tư+vấn" \
-H "X-Auth-Email: user@company.com"
POST /kb/categories/{category_id}/records
Description: Thêm mới bản ghi. Record được tạo với trạng thái "pending". Mã record (record_code) được auto-generate.
Request Body:
{
"data": {
"Tên dịch vụ": "Tư vấn kỹ thuật",
"Loại dịch vụ": "Dịch vụ hỗ trợ",
"Mức độ ưu tiên": "Cao"
}
}
Validation:
data phải khớp với cột được định nghĩa trong danh mụcrequired=true, field đó bắt buộc phải có giá trịResponse:
{
"success": true,
"data": [
{
"record_id": "507f191e810c19729de860ee",
"record_code": "DL_51",
"business_status": "pending"
}
],
"length": 1
}
Example:
curl -X POST "https://api.example.com/ai-service/api/kb/categories/507f1f77bcf86cd799439011/records" \
-H "Content-Type: application/json" \
-H "X-Auth-Email: user@company.com" \
-d '{
"data": {
"Tên dịch vụ": "Tư vấn kỹ thuật",
"Loại dịch vụ": "Dịch vụ hỗ trợ",
"Mức độ ưu tiên": "Cao"
}
}'
PUT /kb/categories/{category_id}/records/{record_id}
Description: Sửa bản ghi (chỉ field data, status không đổi).
Request Body:
{
"data": {
"Tên dịch vụ": "Tư vấn kỹ thuật (cập nhật)",
"Loại dịch vụ": "Dịch vụ hỗ trợ"
}
}
Response:
{
"success": true,
"data": [
{
"record_id": "507f191e810c19729de860ee",
"record_code": "DL_51"
}
],
"length": 1
}
PUT /kb/categories/{category_id}/records/activate
Description: Kích hoạt bản ghi (pending → active). Trigger ingest vào vector store. Bulk operation.
Request Body:
{
"record_ids": [
"507f191e810c19729de860ee",
"507f191e810c19729de860ef"
]
}
Response:
{
"success": true,
"data": [
{
"activated_count": 2,
"ingest_count": 2,
"batch_id": "batch_20260616_001"
}
],
"length": 1
}
Notes:
PUT /kb/categories/{category_id}/records/deactivate
Description: Vô hiệu hóa bản ghi (active → pending). Bulk operation.
Request Body:
{
"record_ids": [
"507f191e810c19729de860ee"
]
}
Response:
{
"success": true,
"data": [
{
"deactivated_count": 1
}
],
"length": 1
}
interface KBCategory {
category_id: string; // MongoDB ObjectId
category_code: string; // DM_XXX (auto-generated)
name: string; // Tên danh mục (unique per tenant)
description?: string; // Mô tả
created_at: string; // ISO 8601 timestamp
created_by: string; // Email người tạo
updated_at: string; // ISO 8601 timestamp
updated_by: string; // Email người cập nhật
}
type ColumnDataType =
| "textbox"
| "textarea"
| "number"
| "dropdown"
| "multi_select"
| "email";
interface KBColumnDef {
column_id: string; // MongoDB ObjectId
category_id: string; // Reference to KBCategory._id
name: string; // Tên cột (unique per category)
data_type: ColumnDataType; // Kiểu dữ liệu
required: boolean; // Có bắt buộc hay không
description?: string; // Mô tả
order: number; // Thứ tự hiển thị
source_config?: {
type: "data_source" | "manual";
// For data_source:
category_id?: string; // Reference category
display_column_id?: string; // Display column
// For manual:
values?: string[]; // Hardcoded options
};
created_at: string;
created_by: string;
updated_at: string;
updated_by: string;
}
type BusinessStatus = "pending" | "active";
interface KBDataRecord {
record_id: string; // MongoDB ObjectId
record_code: string; // DL_N (auto-generated)
category_id: string; // Reference to KBCategory._id
data: {
[key: string]: any; // Dynamic fields per column
};
business_status: BusinessStatus;
created_at: string;
created_by: string;
updated_at: string;
updated_by: string;
}
// 1. Create category
const categoryRes = await fetch('/ai-service/api/kb/categories', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Auth-Email': 'user@company.com',
'X-Auth-User-ID': 'xxx',
'X-Tenant-Id': '2122030000'
},
body: JSON.stringify({
name: "Loại dịch vụ",
description: "Danh mục các loại dịch vụ"
})
});
const category = await categoryRes.json();
const categoryId = category.data[0].category_id;
// 2. Create columns
const col1Res = await fetch(
`/ai-service/api/kb/categories/${categoryId}/columns`,
{
method: 'POST',
headers: { /* ... */ },
body: JSON.stringify({
name: "Tên dịch vụ",
data_type: "textbox",
required: true,
order: 1
})
}
);
// 3. Create record
const recordRes = await fetch(
`/ai-service/api/kb/categories/${categoryId}/records`,
{
method: 'POST',
headers: { /* ... */ },
body: JSON.stringify({
data: {
"Tên dịch vụ": "Tư vấn kỹ thuật"
}
})
}
);
const record = await recordRes.json();
console.log('Record created:', record.data[0].record_code); // DL_1
// 4. Activate record
await fetch(
`/ai-service/api/kb/categories/${categoryId}/records/activate`,
{
method: 'PUT',
headers: { /* ... */ },
body: JSON.stringify({
record_ids: [record.data[0].record_id]
})
}
);
// Create dropdown that pulls data from another category
const dropdownRes = await fetch(
`/ai-service/api/kb/categories/cat-main-id/columns`,
{
method: 'POST',
headers: { /* ... */ },
body: JSON.stringify({
name: "Loại dịch vụ",
data_type: "dropdown",
source_config: {
type: "data_source",
category_id: "cat-service-type-id",
display_column_id: "col-service-name-id"
}
})
}
);
// Search for records created in June 2026, status active
const searchRes = await fetch(
`/ai-service/api/kb/categories/cat-id/records?` +
`status=active&` +
`created_from=2026-06-01&` +
`created_to=2026-06-30&` +
`keyword=tư+vấn&` +
`page=1&page_size=50`,
{
headers: { /* ... */ }
}
);
const results = await searchRes.json();
console.log(`Total records: ${results.total_record}`);
Always validate tenant context: Endpoints automatically filter by tenant from headers. Never pass tenant_id in URL.
Handle pagination: Implement pagination in FE to avoid loading too many records at once.
Check references before delete: Always call GET references endpoint before DELETE to prevent errors.
Bulk activate/deactivate: Use bulk endpoints for multiple records to avoid N+1 requests.
Error handling: Always check success field in response. Don't assume success based on HTTP 200.
Audit trail: All modifications (create/update/delete) are logged in KBAuditLog collection with actor email and before/after data.
Last Updated: 2026-06-22
API Version: 1.0