CTAS Work API — v1 (bản nháp tài liệu cho developer.ctas.vn/docs/ctas-work)

Trạng thái: bản nháp 2, 06/09/2026, chưa phát hành. Nguồn sự thật khi phát hành là file OpenAPI ctas-api/docs/openapi/work.v1.yaml; tài liệu này là phần *hướng dẫn* bao quanh. Phiên bản API mô tả ở đây: 2026-09-01.

1. Giới thiệu

CTAS Work là hệ thống chấm công, lịch làm việc, nhân sự và lương cho doanh nghiệp. API cho phép phần mềm của bạn (POS, ERP, kế toán, máy chấm công…) đọc và ghi dữ liệu của các tổ chức đã kết nối với ứng dụng của bạn trên CTAS Work, và nhận thông báo khi có thay đổi.

Hai cách dùng, cùng một API:

Ứng dụng nội bộỨng dụng nền tảng
Bạn làdoanh nghiệp tự tích hợp hệ thống của mình với CTAS Worknhà cung cấp phần mềm có nhiều khách hàng dùng CTAS Work
Kết nối tớichính tổ chức của bạnmọi tổ chức đồng ý kết nối, mỗi tổ chức một kết nối
Cần duyệtkhôngcó, trước khi kết nối tổ chức ngoài tổ chức của bạn

Bạn có thể:

  • Đọc phòng ban, chức danh, nhân sự; tạo và cập nhật nhân sự.
  • Đọc chấm công và lịch làm việc theo khoảng ngày.
  • Định danh nhân sự bằng mã QR: phần mềm của bạn hiện mã QR, nhân viên quét bằng ứng dụng CTAS Work, bạn nhận về mã nhân sự của người đó — không cần tự dựng đăng nhập cho nhân viên.
  • Kích hoạt lượt việc trong Quy trình kèm dữ liệu mồi sẵn.
  • Nhận webhook có chữ ký cho mọi sự kiện trên.

Điều kiện

Sandbox (client test)Production (client live)
Tài khoản CTAS tạo ứng dụng là quản trị viên của một tổ chức
Tổ chức được kết nối ở gói Doanh nghiệp (Enterprise)không cầnbắt buộc
Ứng dụng nền tảng đã được CTAS duyệt phát hànhkhông cầnbắt buộc để kết nối tổ chức khác
Dữ liệutổ chức sandbox, tạo bao nhiêu cũng được, reset đượctổ chức thật
Trần mặc định300/phút/ứng dụng · 60/phút/kết nối3 000/phút/ứng dụng · 600/phút/kết nối

2. Bắt đầu trong 10 phút

  1. Đăng nhập developer.ctas.vn bằng tài khoản CTAS → chọn tổ chức → Tạo ứng dụng → sản phẩm CTAS Work, loại *nội bộ* hoặc *nền tảng*, chọn scope xin.
  2. Tab Thông tin xác thực › Test: sao chép client_id (ctas_ci_test_…) và client_secret (ctas_cs_test_…). Secret hiện một lần.
  3. Tab Sandbox › Tạo tổ chức sandbox → nhận một tổ chức giả (3 phòng ban, 12 nhân sự, 30 ngày chấm công). Bấm Tạo mã kết nối ngay tại đó → CTAS-7K2M-9Q4D.
  4. Đổi mã lấy token:
curl -X POST https://api.ctas.vn/api/developer/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "urn:ctas:params:oauth:grant-type:connection_code",
    "code": "CTAS-7K2M-9Q4D",
    "client_id": "ctas_ci_test_…",
    "client_secret": "ctas_cs_test_…"
  }'
{
  "access_token": "ctas_at_…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "ctas_rt_…",
  "scope": "organization:read members:read identity:write",
  "connection": { "id": "con_…", "organization_id": "org_…", "environment": "test" }
}
  1. Gọi thử:
curl https://api.ctas.vn/api/developer/v1/work/organization \
  -H "Authorization: Bearer ctas_at_…"
  1. Mở docs khi đang đăng nhập: khung Thử ngay cạnh mỗi endpoint đã gắn sẵn token test của kết nối sandbox.

3. Xác thực

3.1 Ba loại chứng thư

Là gìSốngDùng ở đâu
client_id + client_secretdanh tính ứng dụng của bạn, mỗi môi trường một cặpcho đến khi xoaychỉ trong POST /oauth/token, chỉ từ backend
Access token ctas_at_…vé gọi API, gắn với một kết nối (một tổ chức) hoặc với ứng dụng1 giờheader Authorization: Bearer
Refresh token ctas_rt_…lấy access token mới cho kết nối đó90 ngày, gia hạn mỗi lần dùngchỉ trong POST /oauth/token

Không bao giờ dùng client_secret hay refresh token ở ứng dụng di động, mã chạy trên trình duyệt, hay commit vào git. Tiền tố ctas_cs_ / ctas_rt_ được công cụ quét bí mật nhận diện.

3.2 Kết nối là gì

Một kết nối (con_…) là sự cho phép của một tổ chức cho ứng dụng của bạn, với một tập scope, ở một môi trường. Quản trị viên tổ chức cấp nó, và có thể thu hồi bất cứ lúc nào trong ứng dụng CTAS Work. Mọi access token đọc dữ liệu tổ chức đều thuộc về đúng một kết nối, nên phần mềm của bạn phục vụ nhiều khách hàng phải giữ một cặp token cho mỗi kết nối.

Mọi response dữ liệu tổ chức kèm header X-CTAS-ConnectionX-CTAS-Organization — hãy đối chiếu trước khi ghi vào CSDL của khách.

3.3 Cách một tổ chức kết nối với ứng dụng của bạn

Cách A — Mã kết nối (khuyến nghị). Quản trị viên mở CTAS Work › Tổ chức › Tích hợp › Kết nối ứng dụng, chọn ứng dụng của bạn (hoặc nhập mã app_… bạn đưa), xem scope, bấm Tạo mã kết nối. Họ nhận một mã dạng CTAS-7K2M-9Q4D, hiệu lực 10 phút, dùng một lần. Phần mềm của bạn cho họ dán mã vào, backend của bạn đổi mã lấy token (mục 3.4). Mã chỉ đổi được bằng client_secret của đúng ứng dụng đó.

Cách B — Uỷ quyền qua web (OAuth authorization code + PKCE). Phần mềm của bạn đưa quản trị viên tới:

https://developer.ctas.vn/oauth/authorize
  ?client_id=ctas_ci_live_…
  &redirect_uri=https://app.example.com/ctas/callback
  &scope=organization:read%20members:read
  &state=…&code_challenge=…&code_challenge_method=S256

Họ đăng nhập, chọn tổ chức (chỉ nơi họ là quản trị viên và đủ gói), đồng ý, và được đưa về redirect_uri?code=…&state=…. redirect_uri phải khớp tuyệt đối một URL đã đăng ký trong console. Đổi code lấy token với grant_type=authorization_code + code_verifier.

Cả hai cách tạo ra cùng một kết nối; sự kiện connection.created được gửi tới webhook của bạn kèm organization_id.

3.4 POST /oauth/token

Nhận JSON hoặc application/x-www-form-urlencoded. Trả lời theo RFC 6749.

grant_typeTham sốTrả về
client_credentialsclient_id, client_secret, scope?token ứng dụng (ứng dụng nền tảng: chỉ gọi /connections, /webhooks, /events) — hoặc token kết nối duy nhất (ứng dụng nội bộ)
urn:ctas:params:oauth:grant-type:connection_codecode, client_id, client_secrettoken kết nối + refresh token
authorization_codecode, code_verifier, redirect_uri, client_id, client_secrettoken kết nối + refresh token
refresh_tokenrefresh_token, client_id, client_secret, scope? (chỉ được hẹp hơn)cặp token mới; refresh token cũ hết hiệu lực sau 60 giây

Lỗi: {"error":"invalid_client"|"invalid_grant"|"invalid_scope"|"unsupported_grant_type", "error_description":"…"} với HTTP 400/401. Mọi thất bại xác thực client trả cùng invalid_client.

3.5 Vòng đời token — bạn phải xử lý

gọi API → 401 TOKEN_EXPIRED
   → POST /oauth/token grant_type=refresh_token
       → 200: lưu CẢ HAI token mới, gọi lại
       → 400 invalid_grant: kết nối đã bị thu hồi, hết hạn, hoặc refresh token đã bị dùng
                            → đánh dấu kết nối đứt, yêu cầu quản trị viên cấp mã kết nối lại
  • Refresh token dùng một lần. Cái cũ còn được chấp nhận trong 60 giây để hai tiến trình cùng refresh không đá nhau; sau đó, dùng lại refresh token cũ bị coi là rò rỉ: toàn bộ token của kết nối bị thu hồi và bạn nhận connection.compromised. Hãy khoá (mutex) thao tác refresh theo kết nối phía bạn.
  • Chủ động refresh khi còn dưới 5 phút thay vì chờ 401.
  • POST /oauth/revoke { "token": "…" } khi khách hàng ngừng dùng phần mềm của bạn.
  • POST /oauth/introspect { "token": "…" }{ "active": true, "scope": "…", "connection_id": "con_…", "organization_id": "org_…", "expires_at": "…" } — để debug.

3.6 Scope

ScopeCho phép
organization:readtổ chức, phòng ban, chức danh
members:readdanh sách và chi tiết nhân sự (không lương, không giấy tờ)
members:writetạo, cập nhật, cho nghỉ nhân sự; cấp mã kích hoạt
members:salarythêm các trường lương cơ bản vào nhân sự
attendance:readchấm công, lịch làm việc
leave:read / leave:writeđơn nghỉ và duyệt/từ chối
process:read / process:writebiểu mẫu, phân công, phiếu; kích hoạt lượt việc
identity:writetạo và theo dõi yêu cầu định danh QR
webhooks:managequản lý webhook bằng API (token ứng dụng)

Scope xin khi phát hành ≥ scope tổ chức cấp khi kết nối ≥ scope trong token. Xin members:salary khi phát hành phải giải thích lý do.

Không scope nào trả về: giấy tờ tuỳ thân, địa chỉ thường trú, tài khoản ngân hàng, mã số thuế cá nhân, phiếu lương, email/số điện thoại đăng nhập, id người dùng CTAS.


4. Quy ước

4.1 Base URL và phiên bản

https://api.ctas.vn/api/developer/oauth/…        xác thực
https://api.ctas.vn/api/developer/v1/…           token ứng dụng: /app, /connections, /webhooks, /events
https://api.ctas.vn/api/developer/v1/work/…      token kết nối: dữ liệu tổ chức
  • v1 là phiên bản lớn. Mỗi ứng dụng ghim một ngày api_version (ví dụ 2026-09-01) lúc tạo; mọi response mang X-CTAS-Api-Version.
  • Thêm trường/enum/endpoint: không đổi ngày, không phá vỡ — hãy bỏ qua trường lạ.
  • Xoá trường, đổi nghĩa: ra ngày mới; ứng dụng cũ giữ nguyên hành vi đến khi bạn đổi trong console. Ngày sắp ngừng có header DeprecationSunset trước ≥ 90 ngày.

4.2 Mã định danh

Tiền tốĐối tượng
app_ứng dụng
con_kết nối
org_tổ chức
dep_ / pos_phòng ban / chức danh
mem_nhân sự — một người trong một tổ chức
idr_yêu cầu định danh QR
whk_ / dlv_ / evt_webhook / lần giao / sự kiện

mem_ là mã theo tổ chức: cùng một người ở hai tổ chức có hai mã khác nhau, không suy ra được tài khoản CTAS. Đây là mã bạn nên lưu.

4.3 Định dạng dữ liệu

LoạiĐịnh dạng
ngàyYYYY-MM-DD
ngày-giờISO-8601 có múi giờ
giờ trong caHH:mm
tiềnsố nguyên theo currency_code của tổ chức
ngày công / phépsố thực 1 chữ số lẻ

Body application/json, tối đa 1 MB. Trường không có trong tài liệu — cả trong body lẫn query — → 422 UNKNOWN_FIELD kèm details.unknown_fields (cố ý, để deparment= không lặng lẽ trả về danh sách chưa lọc).

4.4 Phân trang

GET /members?limit=50            → { "data": [...], "meta": { "next_cursor": "…", "has_more": true } }
GET /members?limit=50&cursor=…

limit 1–100 (mặc định 50); next_cursor null khi hết; con trỏ sống 24 giờ.

4.5 Lỗi (API dữ liệu)

{ "error": { "code": "INSUFFICIENT_SCOPE", "message": "…", "retryable": false,
             "details": { "required_scope": "members:write" }, "request_id": "req_…" } }
HTTPcodeÝ nghĩaThử lại?
401INVALID_TOKENtoken sai hoặc đã thu hồikhông
401TOKEN_EXPIREDhết hạn → refreshsau khi refresh
403INSUFFICIENT_SCOPEthiếu scopekhông
403CONNECTION_REQUIREDdùng token ứng dụng cho endpoint tổ chứckhông
403CONNECTION_REVOKEDtổ chức đã ngắt kết nốikhông
403PLAN_REQUIREDtổ chức không còn ở gói Doanh nghiệp (kết nối được giữ)khi họ mua lại
404NOT_FOUNDkhông có, hoặc thuộc tổ chức kháckhông
409CONFLICT / IDEMPOTENCY_IN_FLIGHTtuỳ
422VALIDATION_FAILED / UNKNOWN_FIELDkhông
429RATE_LIMITEDxem Retry-After
5xxINTERNALcó, backoff

4.6 Idempotency

Idempotency-Key: <uuid> với mọi POST/PATCH/DELETE; cùng key + cùng kết nối trong 24 giờ → trả lại response cũ kèm Idempotent-Replay: true.

4.7 Giới hạn tốc độ

Hai tầng: theo ứng dụng và theo kết nối; header phản ánh tầng chặt hơn:

X-RateLimit-Scope: connection
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 597
X-RateLimit-Reset: 1757127660

/oauth/token giới hạn 10 lần/phút/client. Vượt → 429 + Retry-After.


5. Tài nguyên — token ứng dụng

GET    /app
GET    /connections?status=active|revoked&cursor=
GET    /connections/{id}            DELETE /connections/{id}      (chủ động ngắt)
GET    /webhooks   POST /webhooks   DELETE /webhooks/{id}   POST /webhooks/{id}/rotate   POST /webhooks/{id}/test
GET    /events?since=&connection=&cursor=
{ "id": "con_…", "organization_id": "org_…", "organization_name": "Công ty ABC",
  "environment": "live", "scopes": ["organization:read", "members:read"],
  "status": "active", "granted_at": "…", "last_used_at": "…" }

Ứng dụng nội bộ cũng gọi được các endpoint này bằng token kết nối duy nhất của mình.


6. Tài nguyên — token kết nối (/v1/work)

6.1 Tổ chức

GET /organizationorganization:read

{ "data": { "id": "org_…", "name": "Công ty ABC", "environment": "live",
            "currency_code": "VND", "timezone": "Asia/Ho_Chi_Minh",
            "pay_cycle": "monthly", "cutoff_day": 25, "payday": 5 } }

6.2 Phòng ban & chức danh

GET /departments · GET /departments/{id} · GET /positions

{ "id": "dep_…", "name": "Cửa hàng Quận 1", "kind": "store", "address": "…",
  "lat": 10.77, "lng": 106.70, "radius_m": 100, "lead_member_id": "mem_…" }

6.3 Nhân sự

GET    /members?department=&status=active|paused|left&q=&limit=&cursor=
GET    /members/{id}
POST   /members                      PATCH /members/{id}
POST   /members/{id}/deactivate      POST  /members/{id}/activation-code
{ "id": "mem_Kd7p…", "name": "Nguyễn Văn A", "department_id": "dep_…",
  "position_id": "pos_…", "position": "Thu ngân", "role": "staff", "status": "active",
  "join_date": "2026-03-01", "awaits_activation": false, "created_at": "…", "updated_at": "…" }

Với members:salary thêm base_salary, salary_basis (month|day|hour), rate.

TạoPOST /members (members:write): name, phone (khoá nhận diện tài khoản), department_id, position_id, role, join_date, base_salary, salary_basis. Người chưa có tài khoản CTAS được tạo ở trạng thái chờ kích hoạt và response có meta.activation_code để bạn chuyển cho nhân viên. Vượt trần gói → 403 PLAN_LIMIT (details.limit = "max_active_members"). Trùng số điện thoại trong tổ chức → 409. Không tạo được quản trị viên qua API.

Cho nghỉPOST /members/{id}/deactivate { "effective_date", "reason" }status: left. CTAS Work không xoá hồ sơ nhân sự (lương và chấm công phải giữ), nên không có DELETE.

6.4 Chấm công & lịch

GET /attendance?from=&to=&member=&department=&limit=&cursor=     (tối đa 92 ngày)
GET /schedule?member=&from=&to=                                   (một nhân sự, tối đa 92 ngày)
{ "id": "…", "member_id": "mem_…", "department_id": "dep_…", "date": "2026-09-06",
  "status": "late",
  "check_in": "2026-09-06T08:12:00+07:00", "check_out": "2026-09-06T17:03:40+07:00",
  "sessions": [ { "check_in": "…", "check_out": "…", "verified_by": "geofence" } ],
  "worked_minutes": 531, "late_minutes": 12, "early_leave_minutes": null, "leave_type": null,
  "shift": { "name": "Ca sáng", "start": "08:00", "end": "17:00", "kind": "shift", "standing": false, "credit_day": false } }
  • late_minutesnull khi không có ca để so, không phải 0. shiftnull khi hôm đó không có ca.
  • Chấm công trả theo thứ tự tạo bản ghi và phân trang cursor: đồng bộ dở dang thì tiếp tục đúng chỗ dừng. Không có toạ độ.
  • Lịch trả từng ngày có ca: { "date", "is_working_day", "shift" }, gồm cả ca xếp tay lẫn ca cố định (standing: true). Giá trị ngày công bằng tiền không có ở đây.

6.5 Đơn nghỉ

GET /leave-requests?status=&from=&to= · POST /leave-requests/{id}/approve · POST /leave-requests/{id}/reject { "reason" }

6.6 Quy trình

GET  /process/forms      GET /process/assignments
POST /process/assignments/{id}/trigger
GET  /process/submissions?since=&assignment=&cursor=     GET /process/submissions/{id}

trigger tạo lượt việc ngoài chu kỳ, mồi sẵn dữ liệu để nhân viên chỉ xác nhận:

{ "external_ref": "PO-2026-0912", "scope": { "type": "department", "id": "dep_…" },
  "due_at": "2026-09-06T17:00:00+07:00",
  "context": { "mat_hang": [ { "ten": {"id":"sku_123","label":"Sữa tươi 1L"}, "kl_du_kien": 12 } ] } }

Ảnh trong phiếu là URL có chữ ký, hạn 15 phút.


7. Định danh nhân sự bằng mã QR

7.1 Bài toán

POS của khách hàng cần biết *ai* đang thao tác nhưng không muốn quản lý mật khẩu từng nhân viên. Nhân viên đã có CTAS Work để chấm công. Hãy để họ quét thay vì đăng nhập.

7.2 Luồng

  1. Backend của bạn, bằng token kết nối của tổ chức đó, tạo yêu cầu định danh → nhận qr_text.
  2. Hiển thị qr_text dưới dạng mã QR.
  3. Nhân viên mở CTAS Work › Tổng quan › QR Code (hoặc quét bằng máy ảnh iPhone — mã mở thẳng ứng dụng).
  4. Ứng dụng hiện tên tổ chức, tên ứng dụng của bạn, và dữ liệu sẽ chia sẻ (chỉ mã nhân sự). Nhân viên bấm Đồng ý.
  5. Bạn nhận webhook identity.confirmed — hoặc poll GET /identity-requests/{id}.

7.3 Tạo yêu cầu — POST /identity-requests (identity:write)

{ "reference": "TERMINAL-01", "ttl_seconds": 300,
  "constraints": { "department_ids": ["dep_2Hs8…"], "member_ids": [] } }
{ "data": { "id": "idr_Tt9…", "reference": "TERMINAL-01", "status": "pending",
            "qr_text": "https://developer.ctas.vn/i/8fJ2kQ…",
            "expires_at": "2026-09-06T09:05:00+07:00" } }
TrườngGhi chú
referencechuỗi của bạn (≤ 120), trả lại nguyên vẹn trong webhook
ttl_seconds30–900, mặc định 300
constraints.department_idschỉ nhân sự thuộc các phòng ban/cửa hàng này
constraints.member_idschỉ đúng những người này
ngầm địnhnhân sự đang hoạt động của tổ chức của kết nối

Tối đa 200 yêu cầu pending cùng lúc mỗi kết nối → 429 TOO_MANY_PENDING.

7.4 Theo dõi

GET /identity-requests/{id} · POST /identity-requests/{id}/cancel. status: pendingconfirmed | declined | expired | canceled; khi confirmedmember_id, department_id, confirmed_at.

7.5 Webhook identity.confirmed

{ "id": "evt_01J9…", "type": "identity.confirmed", "api_version": "2026-09-01",
  "created_at": "2026-09-06T09:01:12+07:00",
  "connection_id": "con_…", "organization_id": "org_…",
  "data": { "identity_request_id": "idr_Tt9…", "reference": "TERMINAL-01",
            "member_id": "mem_Kd7p…", "department_id": "dep_2Hs8…",
            "confirmed_at": "2026-09-06T09:01:12+07:00" } }

Payload chỉ có mã nhân sự. Cần tên để hiện lên POS? Gọi GET /members/{member_id} bằng token kết nối có members:read.

7.6 Điều gì không xảy ra

  • Quét không chấm công.
  • Người ngoài tổ chức hoặc không thoả constraints thấy "Mã này không dành cho bạn", không biết mã của tổ chức nào.
  • Một mã dùng đúng một lần.
  • Hiện chỉ ứng dụng iOS hỗ trợ quét.

8. Webhooks

8.1 Đăng ký

Console › Webhooks › Thêm endpoint (theo môi trường) hoặc API với token ứng dụng (webhooks:manage). Một bộ endpoint phục vụ mọi kết nối; mỗi sự kiện mang connection_id + organization_id. Secret whsec_… hiện một lần. Live bắt buộc https://, cấm địa chỉ nội bộ; test nhận http:// để thử qua tunnel.

8.2 Định dạng gửi

POST {url}
Content-Type: application/json
X-CTAS-Event: identity.confirmed
X-CTAS-Event-Id: evt_01J9…-whk_…
X-CTAS-Timestamp: 1757127672
X-CTAS-Signature: t=1757127672,v1=5f1a…
X-CTAS-Api-Version: 2026-09-01

8.3 Kiểm chữ ký — bắt buộc

HMAC-SHA256 với secret của endpoint trên chuỗi "{timestamp}.{raw body}". Từ chối nếu |now − timestamp| > 300s. So sánh bằng hàm thời gian hằng. Dùng raw body, không parse rồi stringify lại.

import crypto from 'node:crypto'
export function verifyCtas(rawBody, headers, secret) {
  const ts = headers['x-ctas-timestamp']
  const sig = Object.fromEntries(headers['x-ctas-signature'].split(',').map(p => p.split('=')))
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false
  const expected = crypto.createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex')
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig.v1 ?? ''))
}
function verifyCtas(string $rawBody, array $headers, string $secret): bool {
    $ts = (int) ($headers['X-CTAS-Timestamp'] ?? 0);
    if (abs(time() - $ts) > 300) return false;
    $parts = [];
    foreach (explode(',', $headers['X-CTAS-Signature'] ?? '') as $p) { [$k, $v] = explode('=', $p, 2) + [null, null]; $parts[$k] = $v; }
    return hash_equals(hash_hmac('sha256', "{$ts}.{$rawBody}", $secret), (string) ($parts['v1'] ?? ''));
}

8.4 Trả lời & thử lại

  • Trả 2xx trong 8 giây; xử lý nặng thì ghi hàng đợi rồi trả 200.
  • Không nghe 2xx (timeout, 5xx, 408, 429): thử lại sau 1s, 5s, 30s, 2 phút, 10 phút; sau 5 lần → failed, gửi lại được trong console.
  • 4xx khác: không thử lại.
  • Hỏng 20 lần liên tiếp → endpoint bị tắt, email chủ ứng dụng.
  • X-CTAS-Event-Id không đổi giữa các lần thử — khử trùng theo id này. Thứ tự không đảm bảo.

8.5 Sự kiện

Sự kiệnKhi
connection.created / connection.revoked / connection.compromisedtổ chức kết nối / ngắt / refresh token bị dùng lại
identity.confirmed / identity.declined / identity.expiredđịnh danh QR
member.created / member.updated / member.leftroster thay đổi (từ app hoặc API)
attendance.checked_in / attendance.checked_outchấm công
leave.requested / leave.decidedđơn nghỉ
process.submission.created / process.submission.reviewed / process.occurrence.missedquy trình

Không có webhook? GET /v1/events?since=&type=&connection=&cursor= trả cùng payload theo thứ tự thời gian, giữ 30 ngày. Token ứng dụng thấy mọi sự kiện của môi trường; token kết nối chỉ thấy sự kiện của kết nối đó.

Bấm Gửi thử trong console (hoặc POST /v1/webhooks/{id}/test) gửi một sự kiện webhook.test ngay lập tức và trả về kết quả lần giao — cách nhanh nhất để kiểm chữ ký trước khi nối thật.


9. Sandbox

  • Tạo bao nhiêu tổ chức sandbox tuỳ ý trong console để giả lập nhiều khách hàng; mỗi tổ chức seed sẵn 3 phòng ban, 4 chức danh, 12 nhân sự (2 chờ kích hoạt), 30 ngày chấm công, 1 biểu mẫu quy trình.
  • Bạn và thành viên ứng dụng là quản trị viên của các tổ chức đó — mở CTAS Work, đổi sang tổ chức sandbox để cấp mã kết nối hoặc quét QR thử bằng chính điện thoại của bạn.
  • Client test chỉ kết nối được tổ chức sandbox; client live chỉ tổ chức thật.
  • Reset trả sandbox về trạng thái seed. Sandbox không gửi thông báo đẩy, không tính vào giới hạn gói, không xuất hiện trong báo cáo.

10. Phát hành ứng dụng nền tảng

Trước khi duyệt, ứng dụng nền tảng chỉ kết nối được tổ chức của bạn và sandbox. Để kết nối tổ chức khác:

  1. Tổ chức của bạn đã xác minh doanh nghiệp trên CTAS Work.
  2. Console › Phát hành: mục đích, website, chính sách bảo mật, giải thích từng scope xin (đặc biệt members:salary).
  3. CTAS xem xét trong 2 ngày làm việc. Sau khi duyệt, ứng dụng xuất hiện trong danh sách "Kết nối ứng dụng" của mọi tổ chức.

Đổi scope xin sau khi phát hành → duyệt lại; các kết nối cũ giữ scope cũ đến khi tổ chức cấp lại.


11. Bảng tra nhanh

members.statusactive · paused · left
connections.statusactive · revoked
identity-requests.statuspending · confirmed · declined · expired · canceled
leave-requests.statuspending · approved · rejected · canceled
attendance.sourceapp · manual · adjusted
deliveries.statuspending · sent · failed

12. Changelog

2026-09-01 (phiên bản đầu)

  • OAuth 2.0 (client_credentials, connection_code, authorization_code + PKCE, refresh_token), kết nối theo tổ chức, tổ chức/phòng ban/chức danh/nhân sự (đọc-ghi), chấm công, lịch, đơn nghỉ, quy trình, định danh QR, webhooks, sổ sự kiện.
  • Chưa có: attendance:write, device flow, SDK. Xem lộ trình tại developer.ctas.vn/roadmap.