Skip to main content
Khi hồ sơ chuyển sang trạng thái quan trọng, hệ thống gửi POST tới URL webhook của bạn. Webhook có chữ ký HMAC, cơ chế retry và khoá idempotency.
Partner không tự đăng ký webhook qua API. Gửi đội ngũ econtractid URL endpoint HTTPS + danh sách event; quản trị viên tạo subscription và cấp HMAC secret (chỉ hiện 1 lần). Đổi URL hoặc event sau này cũng qua quản trị viên.

Luồng gửi


Headers

Mọi payload dùng chung envelope:
Webhook payload không có envelope {statusCode, data, timestamp} như response REST — gửi raw object.

Danh sách event

Event tích hợp (document.*)

Dùng để đồng bộ trạng thái. Bật sẵn mặc định.

Event thông báo (notification.*)

Dùng để uỷ quyền gửi email: thay vì econtractid gửi email thương hiệu của mình, hệ thống bắn webhook kèm danh sách người nhận và ngữ cảnh công việc, để bạn tự render và gửi email theo thương hiệu của mình.
Đây là 4 email thông báo duy nhất hệ thống thực sự gửi. Các event notification.*opt-in — phải đăng ký subscription mới nhận được.
Dự kiến bổ sung: document.signer_signed (từng người ký xong), document.expired (quá hạn).

Payload — document.completed

files[] được thiết kế tương thích tiến: bạn có thể bỏ qua các type chưa nhận diện, cho phép hệ thống thêm loại file mới mà không phá vỡ tích hợp cũ. ecmNodeUuid chính là fileUuid dùng cho endpoint tải file.
signers[]totalSigners chỉ tính người ký bên ngoài — bước của nhân sự nội bộ và bước theo vai trò bị loại khỏi payload. Vì vậy totalSigners có thể nhỏ hơn requiredCount của endpoint tra cứu; đừng đối soát trực tiếp hai con số này.
files[].url là presigned URL — lấy thẳng từ JSON parser rồi GET, đừng đọc từ log thô hay re-marshal (Go mặc định escape &; nếu buộc phải, dùng encoder.SetEscapeHTML(false)). Dấu hiệu sai: URL có %5Cu0026, kho lưu trữ trả AccessDenied dù chưa hết hạn.

Payload — document.rejected

document.rejected vẫn trả về files[]signers[] đầy đủ như document.completed — chỉ khác là chưa có bản ký hoàn chỉnh, và có thêm rejectedBy + reason.

Payload — webhook.test

Event kiểm thử kết nối, bắn khi quản trị viên bấm Test hoặc khi bạn gọi API ở mục dưới. Dùng để nghiệm thu endpoint: URL, TLS, secret, và bạn có trả 2xx không.

Khác biệt so với event thật

Tự bắn ping qua API

Hệ thống gửi webhook.test tới webhook đã đăng ký của tổ chức bạn — giống hệt khi quản trị viên bấm Test.
Dùng API Key theo tổ chức (đã gắn người dùng). Key toàn cục dành cho partner-login, gọi vào đây sẽ nhận 401.
requestPayload / requestHeaders là đúng những gì đã gửi, dùng để đối chiếu khi chữ ký lệch. httpStatus: 0 = không kết nối được hoặc quá 10 giây.

Request thực tế gửi đi

Body trên dây nén và sắp xếp khoá theo alphabet, khác thứ tự khối JSON minh hoạ ở trên. Luôn HMAC trên raw body nhận được, đừng parse rồi serialize lại.

Tự kiểm thử bằng curl

Không cần chờ quản trị viên bấm Test: tự bắn một request giống hệt vào endpoint của bạn. Copy nguyên khối dưới đây, sửa đúng 2 dòng đầu, dán vào terminal (bash hoặc zsh; macOS và Linux đều chạy).
Đạt khi dòng đầu output là HTTP/1.1 2xx. TARGET_URL là endpoint của chính bạn, không phải endpoint của econtractid.

Chưa dựng xong endpoint?

Receiver local xác thực chữ ký thật — in chu_ky_hop_le=True là HMAC hai bên khớp. Sandbox /api/webhook-test/{tag} chỉ ghi lại request và luôn trả 201.
Sandbox chỉ dùng khi onboard: không xác thực chữ ký, ghi nguyên headers + body ra đĩa máy chủ. Đừng gửi dữ liệu thật, đừng trỏ subscription production vào đó.

Vector kiểm chứng chữ ký

Kiểm tra hàm HMAC của bạn không cần gọi mạng. Body mẫu ở trên, secret whsec_demo_secret → chữ ký bắt buộc phải ra:
Ra khác nghĩa là code của bạn thêm/bớt byte — thường do trailing newline hoặc đã parse rồi serialize lại body.

Cách phản hồi

Vẫn xác thực HMAC như event thật, rồi trả 200 mà không chạy nghiệp vụ.

Đọc kết quả


Payload — notification.*

Cùng envelope { id, type, createdAt, data }. Phần data theo nguyên tắc codes-first — không có ID số nội bộ.

recipients[]

Ba biến thể của credentials (external): null (không có tài khoản) · { userName, password: null } (chỉ username) · { userName, password } (có mật khẩu). Người nhận nội bộ luôncredentials: null. emailphone lấy theo đúng thông tin bước ký khai báo. Bước mời người ký bằng số điện thoại sẽ có email: nullphone có giá trị — dùng phone để gửi SMS. Luôn kiểm tra trường nào khác null rồi mới chọn kênh gửi, đừng mặc định là có email.

Ví dụ — notification.task_assigned_external

Event này bắn cho mọi trường hợp giao bước ra ngoài — mời bằng email, bằng số điện thoại, hoặc qua hồ sơ đối tác. Với người ký mời bằng số điện thoại, payload có đủ ba thứ để gửi SMS: phone, credentials, signUrl:
Event này truyền mật khẩu dạng văn bản thuần. Endpoint phải dùng HTTPS, không để công khai, xác thực chữ ký trước khi parse, và không ghi log recipients dạng thô. Gửi hỏng thì người ký không nhận được thông tin đăng nhập — không có cơ chế bù.

Ví dụ — notification.task_assigned_internal

Nhân sự nội bộ được giao bước xử lý. Người nhận luôncredentials: null, và econtractid không ngừng gửi email nội bộ dù tổ chức có uỷ quyền event này.

Ví dụ — notification.task_rejected

Một người ký từ chối. note là lý do từ chối, step là bước bị từ chối, recipients gồm cả người tạo lẫn người tham gia.

Ví dụ — notification.process_finished

Toàn bộ tiến trình hoàn tất. stepnull (không còn bước nào), signUrl cũng null.

Uỷ quyền gửi email

Mặc định econtractid gửi email cho tất cả người nhận. Khi bật uỷ quyền, econtractid ngừng gửi email cho người nhận bên ngoài — nhưng chỉ khi thoả cả hai điều kiện:
  1. Event nằm trong danh sách uỷ quyền của tổ chức (quản trị viên cấu hình)
  2. Tổ chức có ít nhất một webhook subscription đang hoạt động lắng nghe event đó
Chỉ người nhận bên ngoài bị ngừng gửi; nhân sự nội bộ vẫn nhận email từ econtractid. Webhook hết retry mà chưa 2xx thì không có email bù — theo dõi nhật ký gửi trên giao diện quản trị.

Xác thực chữ ký


Retry

Nếu hệ thống của bạn trả về mã khác 2xx hoặc timeout (10 giây), hệ thống sẽ gửi lại: Sau lần thứ 6 thất bại, event được đánh dấu EXHAUSTED. Bạn khôi phục bằng cách gọi GET /integration/documents/{documentId}.

Khuyến nghị triển khai