POST tới URL webhook của bạn. Webhook có chữ ký HMAC, cơ chế retry và khoá idempotency.
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.* là opt-in — phải đăng ký subscription mới nhận được.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.
Payload — document.rejected
document.rejected vẫn trả về files[] và 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ửiwebhook.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.
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
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).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?
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.
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, secretwhsec_demo_secret → chữ ký bắt buộc phải ra:
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ôn có credentials: null.
email và phone 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: null và phone 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
phone, credentials, signUrl:
Ví dụ — notification.task_assigned_internal
Nhân sự nội bộ được giao bước xử lý. Người nhận luôn có credentials: 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. step là null (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:- Event nằm trong danh sách uỷ quyền của tổ chức (quản trị viên cấu hình)
- Tổ chức có ít nhất một webhook subscription đang hoạt động lắng nghe event đó
Xác thực chữ ký
Retry
Nếu hệ thống của bạn trả về mã khác2xx 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}.

