API CRMTop Public API Mở API Console ↓

Tài liệu tích hợp chính thức

Public API v1.

Kết nối hệ thống ngoài với dữ liệu khách hàng, liên hệ, tiềm năng và cơ hội của CRMTop qua OAuth 2.0 Client Credentials.

REST / JSON OAuth 2.0 Cursor pagination 30 endpoints
5 phút đầu tiên

Bắt đầu nhanh

Cấu hình môi trường một lần, lấy access token rồi gọi endpoint cần dùng.

Không thêm dấu / ở cuối.
Chỉ giữ trong bộ nhớ của trang, không lưu vào file hay localStorage.
1

Tạo API client

Trong CRM, tạo client, chọn RunAs user và cấp đúng scopes. Lưu client_idclient_secret.

2

Lấy access token

Gọi POST /connect/token bằng Client Credentials. Có thể dùng form thử nhanh bên dưới.

3

Gọi API

Gửi header Authorization: Bearer <token>. Chọn endpoint và bấm “Thử trong Console”.

OAuth 2.0

Xác thực Client Credentials

Token endpoint nhận dữ liệu dạng application/x-www-form-urlencoded. Nếu bỏ trống scope, server cấp toàn bộ scope đã cấu hình cho client.

Lấy token nhanh

Client secret không được lưu. Không đưa secret vào source code hoặc log.

curl.exe -X POST "https://public.crmtop.vn/connect/token" `
  -H "Content-Type: application/x-www-form-urlencoded" `
  --data-urlencode "grant_type=client_credentials" `
  --data-urlencode "client_id=YOUR_CLIENT_ID" `
  --data-urlencode "client_secret=YOUR_CLIENT_SECRET" `
  --data-urlencode "scope=customers.read"
Token hết hiệu lực: gọi lại token endpoint. Không có refresh token trong Client Credentials flow.
Phân quyền tối thiểu

Scopes và quyền

Token chỉ truy cập được các thao tác đã cấp cho API client và RunAs user cũng phải có quyền tương ứng trong CRM.

Lưu ý: scope *.write dùng chung cho tạo mới và cập nhật. Xóa cần scope *.delete riêng.
Contract v1

Quy ước chung

Header

Authorization: Bearer <access_token>
Content-Type: application/json
Accept: application/json
Idempotency-Key: <unique-key>  # Khuyến nghị cho POST

Ngày giờ và enum

Ngày giờ phải là ISO-8601 có timezone, ví dụ 2026-08-12T09:30:00+07:00 hoặc UTC với Z.

Enum dùng chuỗi camelCase, không dùng số. Ví dụ: customerAndSupplier, closedWon.

PUT là cập nhật đầy đủ

Gửi toàn bộ contract của resource. Không coi PUT là partial update. Field chỉ đọc như id, stage, status, thời gian tạo/sửa không gửi trong body.

Idempotency

Với POST, nên gửi Idempotency-Key dài 1–200 ký tự. Cùng key và cùng request trong 24 giờ sẽ nhận lại kết quả cũ; cùng key nhưng body khác trả 409.

External ID

externalId là ID từ hệ thống tích hợp, duy nhất theo client và resource. Sau khi đã gắn, giá trị này không được đổi.

Rate limit

Token endpoint: 30 request/phút theo IP và client. Business API: 600 request/phút theo tenant và integration. Khi bị giới hạn, chờ theo header Retry-After.

Không bỏ sót dữ liệu

Phân trang bằng Cursor

PublicApiListRequest có ba tham số: pageSize, updatedFromcursor.

Tham sốKiểuMặc địnhÝ nghĩa
pageSizeinteger50Số bản ghi mỗi trang. Server giới hạn từ 1 đến 200.
updatedFromISO-8601 datetimeTừ đầuLấy bản ghi được tạo/cập nhật từ mốc này, theo điều kiện inclusive >=.
cursorstringnullChuỗi opaque từ nextCursor, dùng để đọc trang kế tiếp.
Trang đầu truyền updatedFrom, chưa truyền cursor.
Đọc data, hasMorenextCursor.
Nếu còn dữ liệu, gọi lại với nguyên nextCursor.
Khi hết trang, lưu syncUntilUtc cho phiên sau.
GET /api/v1/customers?pageSize=100&updatedFrom=2026-08-01T00:00:00Z
GET /api/v1/customers?pageSize=100&cursor=<nextCursor>
Vì sao không dùng Skip/Take? Khi dữ liệu thay đổi giữa các trang, vị trí offset bị dịch chuyển và có thể gây trùng hoặc bỏ sót. Cursor dùng thứ tự ổn định (LastModifiedOn, Id), giữ cùng watermark syncUntilUtc và hiệu quả hơn khi đọc sâu.
Incremental sync

Đồng bộ thay đổi và xóa

Change feed trả các sự kiện created, updated, deleted cho từng resource.

Endpoint

GET /api/v1/changes/{resource}

resource: customers | contacts | leads | opportunities

Cần scope {resource}.read.

Chiến lược khuyến nghị

  1. Đọc change feed từ watermark đã lưu.
  2. Với created/updated, gọi GET theo entityId.
  3. Với deleted, xóa hoặc đánh dấu dữ liệu bên nhận.
  4. Lưu syncUntilUtc sau khi hoàn tất toàn bộ trang.
{
  "data": [
    {
      "id": "b7b5f1c3-09cc-43e3-a5c3-512b39329daf",
      "resource": "customers",
      "entityId": "3d1ea89b-f36b-4d48-9520-d9e3d765b997",
      "operation": "updated",
      "occurredOnUtc": "2026-08-12T03:20:15Z",
      "externalId": "ERP-CUS-001"
    }
  ],
  "nextCursor": null,
  "hasMore": false,
  "syncUntilUtc": "2026-08-12T03:21:00Z"
}
Mở rộng theo tenant

Custom fields

Lấy metadata trước khi ghi dữ liệu để biết key, kiểu, bắt buộc và danh sách lựa chọn.

GET /api/v1/metadata/custom-fields?module=customers

[
  {
    "key": "customer_tier",
    "label": "Hạng khách hàng",
    "fieldType": "select",
    "description": null,
    "isRequired": false,
    "options": ["silver", "gold", "diamond"],
    "displayOrder": 1
  }
]
{
  "code": "CUS001",
  "name": "Công ty ABC",
  "customFields": [
    { "key": "customer_tier", "value": "gold" },
    { "key": "credit_limit", "value": 50000000 }
  ]
}
Khi PUT: bỏ thuộc tính customFields để giữ nguyên giá trị hiện có. Gửi mảng để cập nhật các field tương ứng. Key lặp, không tồn tại hoặc đã vô hiệu hóa sẽ trả 400.
Request body

Cấu trúc dữ liệu

Các field dưới đây dùng cho POST và PUT. Response có thêm các field chỉ đọc.

FieldKiểuBắt buộcGhi chú
Field chỉ đọc trong response: tất cả resource có id, createdOnUtc, lastModifiedOnUtc. Contact thêm customerName; Lead thêm status; Opportunity thêm customerName, contactName, leadName, stage.
API reference

Danh sách endpoint

Mở từng endpoint để xem yêu cầu, ví dụ và đưa nhanh vào API Console.

Problem Details

Mã lỗi và xử lý

HTTPÝ nghĩaCách xử lý
400Request, cursor, field hoặc enum không hợp lệ.Đọc detail, errors và sửa request.
401Token/client sai, hết hạn hoặc bị vô hiệu hóa.Lấy token mới; kiểm tra client và RunAs user.
403Thiếu scope, quyền hoặc gói Public API.Cấp scope/quyền phù hợp; kiểm tra subscription.
404Không tìm thấy hoặc không nhìn thấy dữ liệu.Kiểm tra ID và data scope của RunAs user.
409Xung đột external ID, idempotency hoặc concurrency.Không retry mù; đọc code để xử lý.
429Vượt rate limit.Chờ số giây trong header Retry-After.
500Lỗi máy chủ.Retry có backoff và cung cấp traceId khi báo lỗi.
{
  "type": "about:blank",
  "title": "Validation failed",
  "status": 400,
  "detail": "Giá trị ngày giờ phải theo ISO-8601 và có timezone.",
  "instance": "/api/v1/customers",
  "code": "validation_failed",
  "traceId": "00-..."
}
Try it

API Console

Gửi request từ trình duyệt hoặc copy cURL để chạy trong PowerShell. Chọn “Thử trong Console” ở một endpoint để điền tự động.

Nếu trình duyệt báo Failed to fetch: kiểm tra API đang chạy, chứng chỉ HTTPS và cấu hình CORS. Lệnh cURL không phụ thuộc CORS.

Request Builder

Custom request
Chưa gửi
Kết quả response sẽ hiển thị ở đây.