Chuyển tới nội dung chính

Khách hàng

Tổng quan​

Method + pathScopeDùng để
GET /customerscustomers_listLiệt kê để đồng bộ (keyset), hoặc tra một khách theo matchField
GET /customers/:idcustomers_detailLấy một khách theo id
POST /customerscustomers_create + customers_updateTạo hoặc cập nhật (upsert)
PATCH /customers/:id/fieldscustomers_updateGhi trường tuỳ chỉnh
GET /customer-propertiescustomers_listTrạng thái, nguồn, nhóm, ngành, nền tảng của khách
GET /custom-fieldscustom_field_listĐịnh nghĩa trường của khách hàng

Key chỉ thấy những khách mà người tạo key được thấy (theo phạm vi phân quyền của họ). Đường dẫn bên dưới tính từ /api/open/v1 — xem Tổng quan.

Đối tượng khách hàng​

{
"id": "1f0c6a7e-5d1b-4a8e-9a63-2c1f0f6b9d10",
"customerCode": "KH-001",
"fullName": "Nguyễn Thị Lan",
"phoneNumber": "84901234567",
"email": "lan@example.com",
"address": "12 Lý Thường Kiệt, Hà Nội",
"companyName": null,
"notes": null,
"gender": "female",
"dateOfBirth": "1995-04-12",
"status": { "id": 3, "name": "Đang chăm sóc" },
"assignedStaffId": "7a2d…",
"customFields": { "ma_erp": "ERP-778", "hang_thanh_vien": "vang" },
"lastModifiedSource": "api:ERP Kho",
"createdAt": "2026-10-01T02:15:00.000Z",
"updatedAt": "2026-10-08T03:12:45.123Z"
}

Liệt kê khách hàng​

GET /api/open/v1/customers?updatedSince=2026-10-01T00:00:00Z&updatedUntil=2026-10-08T00:00:00Z&limit=50&excludeSource=api:ERP%20Kho

Trả { "data": [khách hàng…], "nextCursor": "…" | null, "window": {…} } — tối đa 50 khách mỗi lần, cửa sổ tối đa 30 ngày. Tham số: xem Phân trang keyset.

Tra một khách​

GET /api/open/v1/customers?matchField=phone&value=0901234567
GET /api/open/v1/customers/1f0c6a7e-5d1b-4a8e-9a63-2c1f0f6b9d10

matchField là phone, email, customerCode, hoặc mã một trường tuỳ chỉnh kiểu văn bản có bật "duy nhất". Trả { "data": khách hàng }, hoặc { "data": null } khi không có. Khớp nhiều hơn một ⇒ 409 OPEN_API_MATCH_AMBIGUOUS.


Tạo hoặc cập nhật (upsert)​

POST /api/open/v1/customers
{
"matchBy": ["customerCode", { "field": "ma_erp" }, "phone"],
"customerCode": "KH-001",
"fullName": "Nguyễn Thị Lan",
"phoneNumber": "0901234567",
"email": "lan@example.com",
"gender": "female",
"dateOfBirth": "1995-04-12",
"statusId": 3,
"customFields": { "ma_erp": "ERP-778", "hang_thanh_vien": "vang" }
}
TrườngKiểuGhi chú
matchBymảng (≤ 5)Thứ tự tiêu chí tìm khách có sẵn: "customerCode", "phone", "email", hoặc { "field": "<mã trường duy nhất>" }
customerCodechuỗi ≤ 50Mã khách tự đặt, duy nhất trong cửa hàng. Vắng khi tạo ⇒ tự sinh
fullName, phoneNumber, email, address, companyName, noteschuỗiTrường mặc định
gendermale / female / other
dateOfBirthYYYY-MM-DD
statusIdsốId trạng thái khách — GET /customer-properties
leadSourceId, leadGroupId, leadIndustryId, leadPlatformIdsốId nguồn / nhóm / ngành / nền tảng — cùng danh mục trên
customFieldsobjectTheo mã trường — xem Trường tuỳ chỉnh

Cách tìm khách có sẵn: đi lần lượt các tiêu chí trong matchBy, bỏ qua tiêu chí không có giá trị trong body, dừng ở tiêu chí đầu tiên khớp đúng một khách. Không gửi matchBy ⇒ mặc định customerCode → các trường tuỳ chỉnh "duy nhất" có trong customFields → phone → email.

  • Khớp ⇒ cập nhật các trường bạn gửi (trường không gửi giữ nguyên). Gửi customerCode khác mã hiện tại ⇒ đổi mã.
  • Không khớp ⇒ tạo khách mới. Body phải đủ các trường bắt buộc theo cấu hình cửa hàng (mặc định: Họ và tên; nhiều cửa hàng bắt buộc thêm Nhóm khách hàng, Nguồn… — thiếu thì trả 400 SCRM_CUSTOMER_FIELD_REQUIRED kèm params.fieldKey).
Dùng mã của hệ thống bạn

Đặt customerCode = mã khách bên hệ thống của bạn (hoặc lưu vào một trường tuỳ chỉnh "duy nhất" như ma_erp) để các lần đồng bộ sau luôn khớp đúng khách, kể cả khi SĐT thay đổi.


Trạng thái và thuộc tính khách​

GET /api/open/v1/customer-properties
{
"data": {
"statuses": [{ "id": 3, "name": "Mới", "code": null, "isActive": true, "isDefault": true }],
"leadSources": [{ "id": 12, "name": "Facebook Ads", "code": "fb_ads", "parentId": null, "isActive": true }],
"leadGroups": [{ "id": 7, "name": "Khách lẻ", "code": "retail", "parentId": null, "isActive": true }],
"leadIndustries": [],
"leadPlatforms": [{ "id": 21, "name": "Zalo", "code": null, "parentId": null, "isActive": true }]
}
}

Gửi id vào statusId / leadSourceId / leadGroupId / leadIndustryId / leadPlatformId khi upsert. Danh mục do cửa hàng cấu hình — nên tải khi khởi động tích hợp và làm mới định kỳ.


Ghi trường tuỳ chỉnh​

PATCH /api/open/v1/customers/1f0c6a7e-5d1b-4a8e-9a63-2c1f0f6b9d10/fields
{ "customFields": { "hang_thanh_vien": "kim_cuong", "ngay_mua_cuoi": "2026-10-07", "ghi_chu_kho": null } }

Chỉ các trường gửi lên bị đổi; null / "" ⇒ xoá giá trị. Trả { "data": khách hàng } sau khi ghi.


Định nghĩa trường​

GET /api/open/v1/custom-fields

Trả mọi trường của khách hàng — trường mặc định (isDefault: true, gửi ở cấp gốc body như fullName) và trường tuỳ chỉnh (gửi trong customFields):

{
"data": [
{ "key": "fullName", "label": "Họ và tên", "type": "text", "required": true, "unique": false, "isDefault": true, "options": null, "relation": null },
{ "key": "ma_erp", "label": "Mã ERP", "type": "text", "required": false, "unique": true, "isDefault": false, "options": null, "relation": null },
{
"key": "hang_thanh_vien", "label": "Hạng thành viên", "type": "select", "required": false, "unique": false, "isDefault": false,
"options": [{ "value": "vang", "label": "Vàng" }, { "value": "kim_cuong", "label": "Kim cương" }],
"relation": null
}
]
}
Đọc định nghĩa trước khi ghi

Mã trường do cửa hàng cấu hình ở Cấu hình → CRM → Trường tuỳ chỉnh. Gọi GET /custom-fields khi khởi động tích hợp để kiểm tra mã trường và lựa chọn hợp lệ, thay vì viết cứng.