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:
https://tickup-api.onrender.com/api/v1Mọ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 = truevà 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.
{
"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-0001cho ứng viên,JOB-2026-0001cho vị trí,PAGE-2026-0001cho 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:
"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ờ:
"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ảifullName). - 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 tenantGET /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ụngGET /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.