Đăng ngày

Pancake CRM Records CRUD API — Quản lý lead / account / contact

Đăng ngày
  • avatar
    Name
    Định Phan - netFull
    Twitter

Pancake CRM Records CRUD API — Quản lý lead / account / contact

TL;DR: Pancake CRM cung cấp 4 endpoint Records cho 3 table types lead / account / contact: GET /workspaces/{ws}/{table}/records (list với filter JSON + cursor pagination), POST /workspaces/{ws}/{table}/records (upsert — có id = update, không id = create, KHÔNG có endpoint create/update riêng), DELETE /workspaces/{ws}/{table}/records?record_ids[]=... (bulk delete), và GET /workspaces/{ws}/record/{id} (single — URL pattern khác list). Filter hỗ trợ 9 operators và logic AND/OR. Lỗi đặc biệt 422 LEAD_CANNOT_UPDATE_FROM_CANCEL — không update được lead đã canceled/converted/merged.

Sau khi đã setup Pancake CRM API + api_key, endpoint dùng nhiều nhất là Records — nơi quản lý lead/contact/account của workspace. Bài này cover đầy đủ 4 endpoint CRUD, filter JSON syntax đầy đủ, và những pitfall thường gặp khi mới integrate.


1. 3 record types — khi nào dùng cái nào?

Pancake CRM theo data model CRM chuẩn industry, tách 3 loại entity với mục đích khác nhau:

TableĐại diệnKhi nào tạoField đặc biệt
leadKhách tiềm năng chưa qualifiedForm landing page submit, FB ad click, import từ data listinterested_product[] — sản phẩm khách quan tâm
contactCá nhân đã verified, có liên hệ thựcSau khi lead được qualified, hoặc khách hàng cá nhân B2CStandard contact fields
accountTổ chức / công ty (B2B)Khi bán cho doanh nghiệp — 1 account có thể link nhiều contactStandard account fields

TIP

Lead → Contact: lifecycle phổ biến — khi lead được sale liên lạc và xác nhận thật, dashboard CRM cho phép convert lead → contact (giữ data, đổi entity type). Endpoint API hiện tại chỉ thao tác từng record trong table riêng — convert là UI-only, chưa expose qua API (theo doc tại thời điểm viết).

Scenario thực tế

  • Shop B2C (mỹ phẩm, thời trang): dùng chủ yếu lead (cho ads) + contact (khách đã mua). account không cần.
  • Agency B2B (marketing, dev studio): dùng account (công ty khách) + contact (người liên hệ trong công ty). Mỗi account có nhiều contact.
  • SaaS bán đa kênh: dùng cả 3 — lead từ FB ad → contact khi demo call → account khi onboard org.

2. Setup nhắc lại

Mọi endpoint trong bài cần:

  • api_key — query param (xem bài CRM intro section 4)
  • workspace_id — path param (xem section 3)
  • table_name — path param, hợp lệ: lead / account / contact (KHÔNG hợp lệ: customer, user, ...)

IMPORTANT

Cả 4 endpoint Records chỉ hỗ trợ 3 table types trên. Pass table_name=customer hay tên khác → 404. Pancake doc không document các table types khác qua API (mặc dù dashboard có).


3. GET /records — list với cursor pagination

GET https://crm.pancake.vn/api/workspaces/{workspace_id}/{table_name}/records?api_key={key}
Tham sốVị tríMô tả
workspace_idPathUUID workspace
table_namePathlead / account / contact
api_keyQueryAPI key per-workspace
filterQueryJSON-encoded filter object (xem section 4)
view_idQueryUUID view saved trong dashboard, hoặc all
cursorQueryCursor từ response trước (pagination)

cURL example — list toàn bộ lead

curl -G "https://crm.pancake.vn/api/workspaces/$WORKSPACE_ID/lead/records" \
  --data-urlencode "api_key=$API_KEY" \
  --data-urlencode "view_id=all"

Response shape

{
  "cursor": "eyJwYWdlIjoyfQ==",
  "entries": [
    {
      "id": "a1b2c3d4-...",
      "name": "Nguyễn Văn A",
      "phone_number": "0901234567",
      "email": "nva@example.com",
      "birthday": "1990-05-12T00:00:00Z",
      "full_address": "Số 126 Hàng Trống, Phường Hoàn Kiếm, Hà Nội, Việt Nam",
      "pancake_tags": ["104943019063372_11"],
      "source": [-11],
      "owner": ["user_uuid_1"],
      "created_on": "2026-06-20T10:30:00Z",
      "modified_on": "2026-06-22T14:15:00Z",
      "interested_product": ["Serum HA", "Cream B5"]
    }
  ]
}

Cursor pagination

Pancake CRM Records dùng cursor-based pagination (giống pattern list conversations chat):

  • Lần đầu: KHÔNG truyền cursor → response trả về cursor của trang kế tiếp
  • Lần sau: truyền cursor từ response cũ → trả 1 trang nữa
  • Hết data: response trả entries: [] hoặc cursor: null/missing → dừng loop
# Lần đầu
curl -G "https://crm.pancake.vn/api/workspaces/$WS/lead/records" \
  --data-urlencode "api_key=$KEY"
# → {"cursor": "abc123...", "entries": [...]}

# Lần sau
curl -G "https://crm.pancake.vn/api/workspaces/$WS/lead/records" \
  --data-urlencode "api_key=$KEY" \
  --data-urlencode "cursor=abc123..."

WARNING

Response không có total_entries hay page_count — bạn không biết trước có bao nhiêu records. Pattern duy nhất: loop tới khi entries rỗng. Nếu cần UI hiển thị tổng số, phải tự đếm bằng cách loop hết.


4. Filter — JSON query với 9 operators

filter là tham số mạnh nhất của endpoint list — JSON-encoded, hỗ trợ logic AND/OR + 9 operators.

Cấu trúc gốc

{
  "search_all": "0972273341",
  "is_group": false,
  "fields": [
    {
      "field_name": "phone_number",
      "type": "$having_value",
      "value": null,
      "is_filter_exclude": false
    }
  ]
}
FieldLoạiMô tả
search_allstringFull-text search trên mọi field text/phone/number. Dùng cho "search box" style
is_groupbooleantrue = OR giữa các điều kiện trong fields[]; false (mặc định) = AND
fields[]arrayDanh sách điều kiện filter
fields[].field_namestringTên field: phone_number, name, email, etc.
fields[].typestringOperator (xem dưới)
fields[].valueanyGiá trị compare — tuỳ operator
fields[].is_filter_excludebooleantrue = NOT logic (negate condition)

9 operators

OperatorMô tảvalue type
$having_valueField có giá trị (không empty)null (omit)
$having_no_valueField rỗngnull (omit)
$enter_valueField bằng/contains valuesingle
$select_multiField match bất kỳ giá trị trong mảngarray
$date_timeField nằm trong range [from, to]array 2 timestamp
$greater_thanNumeric "lớn hơn"string number
$less_thanNumeric "nhỏ hơn"string number
$greater_than_equalNumeric "lớn hơn hoặc bằng"string number
$less_than_equalNumeric "nhỏ hơn hoặc bằng"string number
$associationField reference đến record IDs trong valuearray UUID
$duplicateField bị duplicate qua nhiều recordsnull

Ví dụ 1 — Tìm lead có phone

FILTER='{"fields":[{"field_name":"phone_number","type":"$having_value"}]}'

curl -G "https://crm.pancake.vn/api/workspaces/$WS/lead/records" \
  --data-urlencode "api_key=$KEY" \
  --data-urlencode "filter=$FILTER"

Ví dụ 2 — Lead có phone NHƯNG chưa được liên hệ (OR thay AND)

FILTER='{
  "is_group": true,
  "fields": [
    {"field_name":"phone_number","type":"$having_value"},
    {"field_name":"last_contact","type":"$having_no_value"}
  ]
}'

Ví dụ 3 — Lead từ source FB ad (ID source 10 hoặc 20)

FILTER='{"fields":[{"field_name":"source","type":"$select_multi","value":[10,20]}]}'

Ví dụ 4 — Lead duplicate theo phone (tìm phone trùng)

FILTER='{"fields":[{"field_name":"phone_number","type":"$duplicate"}]}'

Ví dụ 5 — Tìm lead KHÔNG phải từ FB (NOT)

FILTER='{
  "fields": [
    {
      "field_name": "source",
      "type": "$select_multi",
      "value": [10, 20],
      "is_filter_exclude": true
    }
  ]
}'

TIP

URL-encode JSON filter khi truyền qua query string. Trong cURL dùng --data-urlencode (như ví dụ trên) để tool tự encode. Trong code: encodeURIComponent(JSON.stringify(filter)).


5. view_id — pre-defined views từ dashboard

Trong dashboard CRM, user có thể save view với combo filter + sort + columns hiển thị (vd: "Hot leads tuần này", "Contact chưa follow-up 7 ngày"). Mỗi view có UUID.

# Lấy view "all" — bao gồm mọi record không filter
curl -G "https://crm.pancake.vn/api/workspaces/$WS/lead/records" \
  --data-urlencode "api_key=$KEY" \
  --data-urlencode "view_id=all"

# Lấy theo view UUID đã saved trong dashboard
curl -G "https://crm.pancake.vn/api/workspaces/$WS/lead/records" \
  --data-urlencode "api_key=$KEY" \
  --data-urlencode "view_id=550e8400-e29b-41d4-a716-446655440000"

Use case: ở dashboard sales team đã setup view "Hot Lead — phone đầy đủ + chưa close trong 14 ngày", code BI dashboard chỉ cần truyền view_id đó → không phải re-implement filter logic, đồng bộ với UI.


6. POST /records — Upsert pattern

POST https://crm.pancake.vn/api/workspaces/{workspace_id}/{table_name}/records?api_key={key}
Content-Type: application/json

Request body — Record schema

{
  "id": "a1b2c3d4-...",
  "name": "Nguyễn Văn A",
  "phone_number": "0901234567",
  "email": "nva@example.com",
  "birthday": "1990-05-12",
  "full_address": "Số 126 Hàng Trống, Phường Hoàn Kiếm, Hà Nội, Việt Nam",
  "pancake_tags": ["tag_id_1", "tag_id_2"],
  "source": [10],
  "owner": ["user_uuid"],
  "interested_product": ["Serum HA"]
}
FieldBắt buộcMô tả
idUUID. = update record này; không có = create mới
nameTên record — required ngay cả khi update
phone_numberSố điện thoại
emailEmail
birthdayFormat ISO 8601 (YYYY-MM-DD)
full_addressĐịa chỉ đầy đủ. Format VN gợi ý: Số nhà + Tên đường, Phường/Xã, Quận/Huyện, Tỉnh/Thành, Quốc gia
pancake_tags[]Mảng tag IDs
source[]Mảng integer IDs từ Sources API
owner[]Mảng user UUIDs sở hữu record
interested_product[]CHỈ áp dụng với lead — sản phẩm khách quan tâm. Account/contact không có field này

Create — không truyền id

curl -X POST "https://crm.pancake.vn/api/workspaces/$WS/lead/records?api_key=$KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Nguyễn Văn A",
    "phone_number": "0901234567",
    "email": "nva@example.com",
    "source": [10]
  }'

Response:

{
  "success": true,
  "data": {
    "id": "a1b2c3d4-newly-generated",
    "name": "Nguyễn Văn A",
    "phone_number": "0901234567",
    "...": "..."
  }
}

Update — truyền id

curl -X POST "https://crm.pancake.vn/api/workspaces/$WS/lead/records?api_key=$KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "a1b2c3d4-...",
    "name": "Nguyễn Văn A (đã verify)",
    "phone_number": "0987654321"
  }'

WARNING

Lỗi 422 LEAD_CANNOT_UPDATE_FROM_CANCEL — không update được lead đã ở trạng thái canceled / converted / merged. Đây là business rule cứng — kể cả admin cũng không bypass được qua API. Pattern xử lý: check status lead trước khi update, hoặc catch 422 và log để báo lead này đã closed.

Lỗi khác

  • 403 Forbiddenapi_key không có quyền với table này. Check role + workspace ownership.

7. DELETE — bulk delete

DELETE https://crm.pancake.vn/api/workspaces/{workspace_id}/{table_name}/records?api_key={key}&record_ids[]={id1}&record_ids[]={id2}

record_ids[] truyền dạng query string repeat (KHÔNG phải body):

curl -X DELETE "https://crm.pancake.vn/api/workspaces/$WS/lead/records?api_key=$KEY" \
  --data-urlencode "record_ids[]=uuid1" \
  --data-urlencode "record_ids[]=uuid2" \
  --data-urlencode "record_ids[]=uuid3" \
  -G

Response:

{
  "success": true,
  "fallback": "success_only"
}

IMPORTANT

Test với 1 record trước khi bulk — endpoint không có dry-run mode. Soft vs hard delete chưa được doc nói rõ; recommend log lại danh sách record_ids[] đã xoá để recovery nếu cần.


8. GET /record/{id} — single record

URL pattern khác list (singular record/, không phải records), và table_namequery param:

GET https://crm.pancake.vn/api/workspaces/{workspace_id}/record/{record_id}?api_key={key}&table_name={table}
curl -G "https://crm.pancake.vn/api/workspaces/$WS/record/$RECORD_ID" \
  --data-urlencode "api_key=$KEY" \
  --data-urlencode "table_name=lead"

Response:

{
  "success": true,
  "data": {
    "id": "a1b2c3d4-...",
    "name": "Nguyễn Văn A",
    "...": "..."
  }
}

Use case: khi click vào row trong dashboard nội bộ → load chi tiết qua endpoint này (nhanh hơn filter trong list).


9. Pitfalls thường gặp

Triệu chứngNguyên nhânCách fix
404 endpointtable_name không phải lead/account/contactVerify table_name hợp lệ
401 / 403api_key sai hoặc không có quyền với workspaceTạo lại key trong Settings → Tools
422 LEAD_CANNOT_UPDATE_FROM_CANCELLead đã canceled / converted / mergedCheck status trước, hoặc catch error + log
Filter trả về data saiJSON filter syntax lỗiValidate JSON; verify operator name ($having_value chứ không phải having_value)
view_id không tồn tạiUUID sai hoặc view đã bị xoá khỏi dashboardDùng view_id=all làm fallback
interested_product không lưu cho account/contactField chỉ áp dụng cho leadBỏ field này khi POST account/contact
record_ids[] không xoáTruyền dạng body thay vì queryDùng query string với record_ids[] (có dấu [] trong tên param)
Single GET trả 404URL dùng records/ thay vì record/ (mất s), hoặc thiếu table_name queryCheck URL pattern: /record/{id}?table_name=...

10. FAQ

Q: Có endpoint bulk insert không (POST nhiều record cùng lúc)?

A: Không. Pancake CRM hiện chỉ hỗ trợ POST từng record. Bulk import = loop POST + throttle phía client. Pattern khuyến nghị: sleep 100-200ms giữa các request, exponential backoff khi gặp 429/5xx.

Q: Có sync 2 chiều với HubSpot/Salesforce sẵn không?

A: Không có connector chính thức. Tự build qua: HubSpot/Salesforce webhook → Pancake POST upsert + Pancake webhook → HubSpot/Salesforce API. Xem bài 3 phương án truy xuất data Pancake cho pattern integration tương tự (Pancake chat → CRM).

Q: Custom fields (field tự định nghĩa trong dashboard) có xuất hiện trong response không?

A: — custom fields appear ở response với tên field bạn đặt trong dashboard. Field schema trong doc chỉ list standard fields; custom fields cần GET 1 record mẫu để discover schema thực tế của workspace.

Q: Rate limit là bao nhiêu?

A: Doc chính thức không nêu con số. Khuyến nghị throttle conservative: 5-10 req/giây/workspace, exponential backoff khi gặp 429. Test với volume thực tế trước khi production.

Q: Khác nhau giữa record (singular) và records (plural) trong URL?

A: URL convention không nhất quán:

  • /records (plural) = list / upsert / bulk delete
  • /record/{id} (singular) = get single

Dễ nhầm. Khi viết wrapper class, define rõ method names tương ứng.


11. Tổng kết

  • 4 endpoint Records: GET /records (list), POST /records (upsert), DELETE /records (bulk), GET /record/{id} (single — URL khác)
  • 3 table types: lead / account / contactcustomer và tên khác không hợp lệ
  • Upsert pattern: có id = update, không id = create — không có endpoint create/update tách riêng
  • Filter JSON mạnh với 9 operators + AND/OR + NOT — URL-encode khi truyền query
  • view_id: dùng view UUID từ dashboard để đồng bộ filter logic với UI
  • Lỗi đặc biệt 422 LEAD_CANNOT_UPDATE_FROM_CANCEL — lead đã closed không update được, kể cả admin
  • Cursor pagination — loop tới khi entries rỗng, không có total count

Bài viết liên quan

Cluster Pancake CRM

Cluster Pancake chat (sister)

Tài liệu chính thức


Last updated: 2026-06-23. Phiên bản API: Pancake CRM 2.0.0. Tham khảo OpenAPI spec để verify thay đổi.