Skip to content

API Keys

🛠 Dành cho IT / Lập trình viên

Trang này hướng đến bộ phận IT hoặc lập trình viên trong doanh nghiệp của bạn — những người tích hợp CNV Work với hệ thống nội bộ (ERP, kế toán, e-commerce…). Nếu bạn chỉ dùng app bình thường, có thể bỏ qua mục này.

API Keys cho phép hệ thống bên ngoài (server, script, automation tool) gọi REST API của CNV Work mà không cần đăng nhập bằng tài khoản người dùng. Mỗi workspace có thể tạo nhiều key, mỗi key gán một bộ quyền (scope) riêng để hạn chế phạm vi truy cập.

Truy cập

Vào menu Cài đặt > API Keys trên thanh sidebar, hoặc truy cập trực tiếp https://hub.cnvwork.com/api-keys.

Lưu ý

Chỉ vai trò OwnerAdmin mới có quyền tạo, xem và thu hồi API key. Member không nhìn thấy mục này trên sidebar.

Khi nào dùng API Keys

API Key phù hợp với các use case:

  • Server-to-server: backend của bạn đồng bộ dữ liệu định kỳ với CNV Work (ETL, data warehouse)
  • Automation tool: Zapier, Make.com, n8n gọi API để tạo lead khi có form mới
  • Mobile/Desktop app nội bộ: app riêng của công ty đọc/ghi dữ liệu CNV Work
  • Script một lần: import hàng loạt contact, migrate dữ liệu cũ
  • Webhook handler: hệ thống bên ngoài nhận webhook xong gọi ngược API để bổ sung dữ liệu

Nếu bạn cần end-user đăng nhập rồi cấp quyền, hãy dùng OAuth (Mini App) thay vì API Key — xem Marketplace & Mini App.

Tổng quan luồng xác thực

Mỗi request đều được ghi vào audit log: thời gian, IP, endpoint, status code. Bạn xem lại được trong tab Audit log của từng key.

Tạo API Key

  1. Mở trang API Keys
  2. Nhấn + Tạo key
  3. Điền thông tin:
TrườngMô tảBắt buộc
Tên keyTên dễ nhận biết (VD: "Zapier - Lead sync")
Mô tảGhi chú mục đích sử dụngKhông
ScopePhạm vi quyền (xem bảng bên dưới)
IP whitelistDanh sách IP được phép gọi (mỗi dòng 1 IP/CIDR)Không
Ngày hết hạnTự động thu hồi sau ngày nàyKhông
  1. Nhấn Tạo
  2. Copy key ngay lập tức — key chỉ hiển thị duy nhất 1 lần ở dạng đầy đủ

Quan trọng

Sau khi đóng dialog, hệ thống chỉ lưu hash của key — bạn không bao giờ xem lại được key gốc. Nếu lỡ đóng dialog trước khi copy, hãy Thu hồi key cũ và tạo key mới.

Định dạng key

Key có dạng: cnvw_<env>_<random_32_chars>

PhầnÝ nghĩa
cnvw_Prefix nhận diện CNV Work
live hoặc testMôi trường
32 ký tự ngẫu nhiênPhần bí mật

Ví dụ: cnvw_live_4nP9kQ2xW7vB8mZ3hR5tY1jL6dC0sFgA

Scope (phạm vi quyền)

Mỗi key được gán một hoặc nhiều scope. Có 3 cấp độ:

1. Cấp độ workspace

ScopeMô tả
read-onlyChỉ GET, không thay đổi dữ liệu
read-writeGET + POST + PATCH + DELETE toàn bộ module
adminBao gồm cả quản trị (tạo user, đổi role)

2. Scope theo module

Hạn chế key chỉ truy cập một số module:

ScopeModule được phépEndpoint mẫu
crm.*Toàn bộ CRM/leads, /deals, /contacts, /quotations
crm.readCRM chỉ đọcGET /leads, GET /deals
inbox.*Toàn bộ Inbox/conversations, /messages
inbox.sendChỉ gửi tin nhắnPOST /messages
hr.*Toàn bộ HR/employees, /attendance, /leaves
hr.readHR chỉ đọcGET /employees
ticket.*Tickets/tickets, /tickets/:id/comments
approval.*Phê duyệt/approvals, /approvals/:id/decisions
webhook.*Quản lý webhook/webhooks

3. Scope theo hành động cụ thể

Chỉ cho phép một số endpoint:

  • crm.leads.create — chỉ tạo lead
  • crm.deals.read — chỉ đọc deal
  • inbox.messages.send — chỉ gửi tin nhắn

Mẹo

Áp dụng nguyên tắc least privilege: cấp scope hẹp nhất đủ dùng. Ví dụ integration Zapier chỉ cần tạo lead → chọn crm.leads.create, không cần crm.*.

Xác thực request

Mọi request đến https://api.cnvwork.com/api/v1 phải có header:

http
Authorization: Bearer cnvw_live_4nP9kQ2xW7vB8mZ3hR5tY1jL6dC0sFgA
Content-Type: application/json

Nếu thiếu hoặc sai key, API trả 401 Unauthorized:

json
{
  "error": "unauthorized",
  "message": "Invalid or missing API key"
}

Nếu key đúng nhưng không đủ scope, API trả 403 Forbidden:

json
{
  "error": "forbidden",
  "message": "API key lacks required scope: crm.deals.write"
}

Rate limit

Mỗi workspace bị giới hạn:

GóiRate limitBurst
Free100 req/giờ20 req/phút
Pro1.000 req/giờ100 req/phút
Business10.000 req/giờ500 req/phút
EnterpriseTheo hợp đồng

Response header trả về thông tin rate limit:

http
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 873
X-RateLimit-Reset: 1714521600

Khi vượt limit, API trả 429 Too Many Requests:

json
{
  "error": "rate_limit_exceeded",
  "message": "Workspace exceeded 1000 req/h. Retry after 234 seconds.",
  "retry_after": 234
}

Client nên đọc header Retry-After và backoff tương ứng.

Ví dụ code

cURL

GET danh sách lead:

bash
curl -X GET 'https://api.cnvwork.com/api/v1/leads?limit=20&status=new' \
  -H 'Authorization: Bearer cnvw_live_4nP9kQ2xW7vB8mZ3hR5tY1jL6dC0sFgA' \
  -H 'Content-Type: application/json'

POST tạo lead mới:

bash
curl -X POST 'https://api.cnvwork.com/api/v1/leads' \
  -H 'Authorization: Bearer cnvw_live_4nP9kQ2xW7vB8mZ3hR5tY1jL6dC0sFgA' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Nguyễn Văn A",
    "phone": "0901234567",
    "email": "anguyen@example.com",
    "source": "website",
    "note": "Quan tâm gói Pro"
  }'

PATCH cập nhật lead:

bash
curl -X PATCH 'https://api.cnvwork.com/api/v1/leads/lead_abc123' \
  -H 'Authorization: Bearer cnvw_live_4nP9kQ2xW7vB8mZ3hR5tY1jL6dC0sFgA' \
  -H 'Content-Type: application/json' \
  -d '{
    "status": "contacted",
    "owner_id": "user_xyz789"
  }'

Node.js (axios)

js
const axios = require('axios');

const cnv = axios.create({
  baseURL: 'https://api.cnvwork.com/api/v1',
  headers: {
    Authorization: `Bearer ${process.env.CNV_API_KEY}`,
    'Content-Type': 'application/json',
  },
  timeout: 10000,
});

// GET danh sách lead
async function listLeads() {
  const { data } = await cnv.get('/leads', {
    params: { limit: 20, status: 'new' },
  });
  return data.items;
}

// POST tạo lead
async function createLead(payload) {
  const { data } = await cnv.post('/leads', payload);
  return data;
}

// PATCH cập nhật lead
async function updateLead(id, patch) {
  const { data } = await cnv.patch(`/leads/${id}`, patch);
  return data;
}

(async () => {
  try {
    const leads = await listLeads();
    console.log(`Found ${leads.length} new leads`);

    const lead = await createLead({
      name: 'Nguyễn Văn A',
      phone: '0901234567',
      email: 'anguyen@example.com',
      source: 'website',
    });
    console.log('Created lead:', lead.id);

    await updateLead(lead.id, { status: 'contacted' });
    console.log('Updated to contacted');
  } catch (err) {
    if (err.response?.status === 429) {
      const retry = err.response.headers['retry-after'];
      console.log(`Rate limited. Retry after ${retry}s`);
    } else {
      console.error(err.response?.data || err.message);
    }
  }
})();

Python (requests)

python
import os
import requests

API_BASE = 'https://api.cnvwork.com/api/v1'
API_KEY = os.environ['CNV_API_KEY']

session = requests.Session()
session.headers.update({
    'Authorization': f'Bearer {API_KEY}',
    'Content-Type': 'application/json',
})

# GET danh sách lead
def list_leads(status='new', limit=20):
    r = session.get(f'{API_BASE}/leads', params={'status': status, 'limit': limit})
    r.raise_for_status()
    return r.json()['items']

# POST tạo lead
def create_lead(payload):
    r = session.post(f'{API_BASE}/leads', json=payload)
    r.raise_for_status()
    return r.json()

# PATCH cập nhật lead
def update_lead(lead_id, patch):
    r = session.patch(f'{API_BASE}/leads/{lead_id}', json=patch)
    r.raise_for_status()
    return r.json()

if __name__ == '__main__':
    leads = list_leads()
    print(f'Found {len(leads)} new leads')

    lead = create_lead({
        'name': 'Nguyễn Văn A',
        'phone': '0901234567',
        'email': 'anguyen@example.com',
        'source': 'website',
    })
    print('Created lead:', lead['id'])

    update_lead(lead['id'], {'status': 'contacted'})
    print('Updated to contacted')

Quản lý vòng đời key

Xem danh sách key

CộtMô tả
TênTên key bạn đặt
Prefix8 ký tự đầu (VD: cnvw_liv...)
ScopeTóm tắt phạm vi quyền
Người tạoAi tạo key
Ngày tạoTimestamp tạo
Lần dùng cuốiLần cuối key được sử dụng
Trạng tháiActive / Revoked / Expired

Rotate (xoay) key

Khi nghi ngờ key bị lộ hoặc theo lịch định kỳ (khuyến nghị 90 ngày/lần):

  1. Vào trang API Keys
  2. Nhấn + Tạo key mới với cùng scope
  3. Cập nhật key mới vào hệ thống dùng key (env var, secrets manager)
  4. Quay lại CNV Work, nhấn Thu hồi key cũ
  5. Quan sát audit log để chắc chắn không có request nào dùng key cũ trước khi xoá

Thu hồi (revoke) key

  1. Nhấn vào key trong danh sách
  2. Chọn Hành động > Thu hồi
  3. Xác nhận

Sau khi thu hồi, mọi request dùng key đó trả 401 Unauthorized ngay lập tức (không có grace period).

Lưu ý

Thu hồi key không thể hoàn tác. Hệ thống dùng key sẽ ngừng hoạt động ngay lập tức.

Audit log

Mở chi tiết key > tab Audit log xem 30 ngày gần nhất:

CộtMô tả
Thời gianTimestamp request
Method + EndpointVD: GET /leads
Status200, 401, 429, 500...
IPIP gọi request
User-AgentClient (axios, python-requests, ...)
LatencyThời gian xử lý (ms)

Lọc theo status code để phát hiện bất thường:

  • Nhiều 401 → key bị lộ, đang bị thử dò
  • Nhiều 429 → cần upgrade gói hoặc thêm backoff
  • Nhiều 500 → contact support

Best practice bảo mật

Khuyến nghị

  1. Lưu key trong secrets manager (AWS Secrets Manager, HashiCorp Vault, Doppler) — không hardcode, không commit vào Git
  2. Mỗi integration một key riêng — dễ revoke khi cần, dễ truy vết
  3. Bật IP whitelist khi gọi từ server cố định
  4. Đặt expiry date cho key tạm thời (script migrate dữ liệu, demo)
  5. Rotate định kỳ 90 ngày/lần
  6. Dùng scope hẹp — không cấp admin nếu chỉ cần crm.leads.create
  7. Monitor audit log hàng tuần — đặc biệt status 401 bất thường
  8. Thu hồi ngay khi nhân sự nghỉ việc hoặc đổi integration

Lỗi thường gặp

LỗiNguyên nhânXử lý
401 unauthorizedSai key hoặc key bị revokeKiểm tra key, tạo lại nếu cần
403 forbiddenKey thiếu scopeMở key, thêm scope tương ứng
403 ip_not_allowedIP không nằm trong whitelistThêm IP vào whitelist hoặc xoá whitelist
429 rate_limit_exceededVượt rate limitBackoff theo Retry-After, hoặc upgrade gói
500 internal_errorLỗi serverThử lại sau, báo support kèm X-Request-ID

Liên kết liên quan

CNV Work — Nền tảng SaaS đa workspace cho doanh nghiệp