Nhà phát triển
Receiver endpoint
Endpoint website phải dựng — GET handshake năng lực + POST nhận bài.
Website conform CPP v1 = expose một endpoint, hai method:
GET {endpoint}→ khai báo năng lực (handshake). Không tác dụng phụ.POST {endpoint}→ nhận một bài (Envelope).
Cả hai được ký Standard Webhooks (HMAC-SHA256) bằng CITELY_WEBHOOK_SECRET.
1. GET — Handshake năng lực
Trước khi publish (và ở nút "Test Connection"), Citely gọi GET để học website là loại
gì rồi mới gửi payload đúng khẩu vị — Citely không phải đoán framework.
Response 200 application/json:
{
"citely": "receiver",
"protocol": 1, // bắt buộc = 1. Khác → Citely trả CPP_VERSION_UNSUPPORTED
"profile": "static-git", // "runtime-db" | "static-git" | "static-fs" | "citely-git"
"storageAdapter": "git", // "db" | "git" | "fs-build" | "citely-git"
"accepts": ["markdown"], // định dạng bạn tiêu hoá: "html" và/hoặc "markdown"
"locales": ["vi", "en"], // ngôn ngữ site hỗ trợ
"async": true, // true = POST trả 202 building; false = 200 live ngay
"fieldMap": { // (tuỳ chọn) để Citely hiển thị/kiểm tra ánh xạ
"excerpt": "description",
"cover": "heroImage",
"locale": "lang"
}
}GETkhông bắt buộc verify chữ ký (không tác dụng phụ, không lộ dữ liệu). Muốn chặt thì verify nhưPOST.
2. POST — Nhận một bài
Thuật toán xử lý (đúng thứ tự)
- Đọc RAW body (bytes nguyên văn) TRƯỚC KHI
JSON.parse— chữ ký ký trên raw. - Verify chữ ký (chi tiết). Sai →
401 CPP_SIG_INVALID. Lệch giờ > 300s →401 CPP_REPLAY. - Kiểm
version. Không hỗ trợ →400 CPP_VERSION_UNSUPPORTED. test === true→ verify xong, trả200 {ok:true}, KHÔNG lưu gì.- Idempotency theo
webhook-id: đã xử lý id này → trả lại kết quả cũ (đừng tạo trùng). Upsert theoexternalId(nếu có), ngược lại theoslug. - Lưu bài (adapter của bạn: DB insert / ghi file + build).
- Trả echo
{ id, url, status }.
Echo (response) khi thành công
{
"id": "string | number", // bắt buộc → Citely lưu, gửi lại làm externalId lần sau
"url": "https://site.com/blog/<slug>", // bắt buộc → permalink công khai
"status": "live" // "live" (đã hiển thị) | "building" (đã nhận, đang rebuild)
}dbadapter →200 { status: "live" }(hiển thị ngay).fs-build→202 { status: "building" }(đã ghi, chờ CI/build). Citely tự pollurltới khi 200 rồi chuyểnlive— xem Trạng thái bất đồng bộ.- Thiếu
status→ Citely mặc định"live"(tương thích ngược).
Lỗi
Trả đúng mã HTTP + JSON { "error": "<CPP_CODE>", "message": "…" }. Bộ mã chuẩn:
Mã lỗi CPP_*.
Checklist implement
-
GETtrả capabilities vớiprotocol: 1+profile/accepts/locales/async. -
POSTđọc RAW body trước khi parse. - Verify chữ ký đúng thuật toán;
401khi sai;401khi lệch giờ > 300s. -
test === true→200, không lưu. - Idempotent theo
webhook-id; upsert theoexternalId(elseslug). - Trả
{ id, url, status };db→ 200 live,fs-build→ 202 building. - Lỗi trả mã
CPP_*chuẩn. -
CITELY_WEBHOOK_SECRETđọc từ env, không hard-code.
Không muốn dựng receiver? Dùng adapter citely-api — Citely host bài, bạn chỉ đọc read-API.