Khái niệm cốt lõi

Quy ước API

Một tập quy ước ngắn áp dụng cho toàn bộ API. Một khi nắm được, mọi endpoint đều trở nên dễ đoán.

URL gốc & versioning

API production nằm tại:

http
https://tickup-api.onrender.com/api/v1

Mọi endpoint đều nằm dưới /api/v1. Chúng tôi sẽ không có thay đổi phá vỡ trong v1: chỉ thêm. Một v2 trong tương lai sẽ tồn tại song song với v1, cho bạn ít nhất 6 tháng để chuyển đổi.

Khi phát triển cục bộ trên bản Tick Up của bạn, cùng các endpoint đó nằm tại http://localhost:8000/api/v1.

HTTP method

Tick Up dùng các động từ REST tiêu chuẩn. Không có PATCH ngoài một số ít endpoint cập nhật một phần — phần lớn mutation là PUT thay thế hoàn toàn các trường tài nguyên bạn quan tâm.

  • GET — đọc. An toàn và idempotent. Không bao giờ có body.
  • POST — tạo, hoặc kích hoạt một hành động (gửi email, lên lịch phỏng vấn). Body là JSON.
  • PUT — cập nhật. Chỉ gửi các trường bạn muốn đổi; các trường không nêu sẽ được giữ nguyên (PUT của Tick Up hành xử như PATCH về mặt ngữ nghĩa — tên theo quy ước REST nhưng ngữ nghĩa thực tiễn).
  • DELETE — xóa mềm. Chúng tôi không bao giờ xóa cứng dữ liệu; bản ghi bị xóa được đánh dấu is_deleted = true và bị loại khỏi các lượt đọc sau.

Kiểu nội dung

Gửi Content-Type: application/json cho mọi request có body. Chúng tôi phản hồi bằng JSON, mã hóa UTF-8.

Hai ngoại lệ là upload file (phân tích CV, avatar ứng viên, asset trang tuyển dụng) dùng multipart/form-data, và pixel theo dõi email trả về PNG 1x1.

Shape phản hồi

Phản hồi thành công là object JSON trần — không bao trong phong bì { data: ... }. Body của GET /candidates/:id chính là ứng viên.

json
{
  "id": "c2a5e7b8-c8aa-4f50-9c3f-2f9a1d9e08aa",
  "code": "CAN-2026-0001",
  "full_name": "Nguyen Minh Tu",
  "email": "tu.nguyen@example.com",
  "created_at": "2026-05-12T08:23:11.043Z"
}

Các list được bao — xem phân trang.

Lỗi

Lỗi trả về body JSON có trường detail. Shape phụ thuộc vào loại lỗi:

{ "detail": "Candidate not found" }

Xem hướng dẫn lỗi để biết danh sách đầy đủ status code và ý nghĩa.

ID

Hai kiểu ID cùng tồn tại trên mỗi tài nguyên:

  • UUID (v4) — primary key. Ổn định, đục, ngẫu nhiên. Dùng trong URL path và foreign key.
  • Mã đọc được — pattern sinh tự động như CAN-2026-0001 cho ứng viên, JOB-2026-0001 cho vị trí, PAGE-2026-0001 cho trang tuyển dụng. Chúng hiển thị cho người dùng và sắp xếp theo thời gian trong mỗi tenant.

Ngày & giờ

Timestamp được trả về dưới dạng chuỗi ISO 8601 ở UTC, độ chính xác mili giây, kết thúc bằng Z:

json
"created_at": "2026-05-12T08:23:11.043Z"

Ngày lịch (ngày sinh, ngày bắt đầu rảnh) là chuỗi ngày ISO trần, không có giờ và múi giờ:

json
"date_of_birth": "1994-08-21"

Gửi ngày theo cùng định dạng. Chúng tôi không chấp nhận Unix epoch timestamp ở bất kỳ chỗ nào.

Đặt tên

  • snake_case cho mọi thuộc tính JSON (full_name, không phải fullName).
  • kebab-case cho các segment URL (/talent-pool, không phải /talentPool).
  • Số nhiều cho tên tài nguyên trong URL (/candidates, /jobs).

Đa tenant

Điều đó bao gồm việc cố truy xuất tài nguyên bằng ID thuộc tenant khác: phản hồi sẽ là 404 Not Found, giống hệt như ID không tồn tại. Chúng tôi không tiết lộ sự tồn tại của bản ghi cross-tenant qua các status code khác nhau.

Xóa mềm

DELETE đặt is_deleted = true và loại bản ghi khỏi các phản hồi list/get. Bản ghi bị xóa vẫn được lưu để audit, nhưng API coi như chúng đã biến mất.

Một số tài nguyên (ứng viên đến từ form công khai, audit log, lịch sử parse) không thể xóa — các cuộc gọi trả về 409 Conflict kèm lý do.

Endpoint công khai vs có xác thực

Phần lớn API cần xác thực. Một số ít endpoint — thường dưới prefix /public — chấp nhận traffic ẩn danh để bạn có thể phục vụ trên website marketing và trang tuyển dụng mà không phơi token ra trình duyệt:

  • GET /api/v1/public/jobs/{tenant_code} — liệt kê vị trí đang mở của một tenant
  • GET /api/v1/public/jobs/{tenant_code}/{job_identifier} — lấy một vị trí theo slug hoặc mã
  • POST /api/v1/public/applications/submit/{tenant_code} — gửi đơn ứng tuyển từ trang tuyển dụng
  • GET /api/v1/public/pages/by-subdomain/{subdomain} — lấy trang tuyển dụng đã xuất bản

Endpoint công khai bị giới hạn tốc độ mạnh hơn theo IP, và có thể yêu cầu token CAPTCHA trong tương lai.

Optimistic concurrency khi cập nhật

Một số endpoint mutation hỗ trợ optimistic concurrency qua header If-Unmodified-Since. Gửi giá trị updated_at bạn đã đọc trước đó khiến API từ chối write với 412 Precondition Failed nếu bản ghi đã thay đổi từ đó. Đây là opt-in — bỏ header đồng nghĩa với last-write-wins.