Citely Docs
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"
  }
}
  • GET khô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ự)

  1. Đọc RAW body (bytes nguyên văn) TRƯỚC KHI JSON.parse — chữ ký ký trên raw.
  2. Verify chữ ký (chi tiết). Sai → 401 CPP_SIG_INVALID. Lệch giờ > 300s → 401 CPP_REPLAY.
  3. Kiểm version. Không hỗ trợ → 400 CPP_VERSION_UNSUPPORTED.
  4. test === true → verify xong, trả 200 {ok:true}, KHÔNG lưu gì.
  5. Idempotency theo webhook-id: đã xử lý id này → trả lại kết quả cũ (đừng tạo trùng). Upsert theo externalId (nếu có), ngược lại theo slug.
  6. Lưu bài (adapter của bạn: DB insert / ghi file + build).
  7. 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)
}
  • db adapter200 { status: "live" } (hiển thị ngay).
  • fs-build202 { status: "building" } (đã ghi, chờ CI/build). Citely tự poll url tới khi 200 rồi chuyển live — 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

  • GET trả capabilities với protocol: 1 + profile/accepts/locales/async.
  • POST đọc RAW body trước khi parse.
  • Verify chữ ký đúng thuật toán; 401 khi sai; 401 khi lệch giờ > 300s.
  • test === true200, không lưu.
  • Idempotent theo webhook-id; upsert theo externalId (else slug).
  • 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.

On this page