Thiết lập webhook trong bảng điều khiển, nhận lượt gửi và xác minh chữ ký HMAC – kèm ví dụ cho curl, Node.js và PHP.
Webhook gửi một yêu cầu HTTP đến máy chủ của bạn ngay khi có điều gì đó xảy ra trong không gian làm việc – ví dụ khi một trang được xuất bản. Nhờ vậy bạn không phải liên tục hỏi xem có gì thay đổi không. Mỗi lượt gửi đều được ký để máy chủ của bạn phân biệt được tin nhắn thật với tin nhắn giả mạo.
Yêu cầu
- Chủ sở hữu hoặc quản trị viên trong không gian làm việc – mục menu “Webhook” chỉ xuất hiện với các vai trò này
- Từ gói Creator Pro; ở Creator Free ghi “Webhook có từ gói Creator Pro.”
- Tối đa 5 webhook ở Creator Pro, 25 ở Creator Agency, 100 ở Enterprise (có thể điều chỉnh)
- Một máy chủ truy cập được qua địa chỉ https công khai
Bạn chỉ quản lý webhook trong bảng điều khiển. Không thể tạo hay thay đổi chúng bằng khóa API.
Tạo webhook
- Mở “Webhook” trong thanh điều hướng
- Nhấp “+ Webhook mới”
- Nhập URL đích và chọn các sự kiện
- Không bắt buộc: nhập mô tả và chọn trạng thái
- Nhấp “Tạo webhook”
- Ở bước “Xong”, sao chép secret ký (“Secret ký – chỉ hiển thị lúc này”) và lưu trên máy chủ – nó chỉ hiển thị lúc này
- Thử webhook bằng “Kiểm thử”. Kết quả nằm trên thẻ cho đến lần kiểm thử tiếp theo: “Gửi thành công” hoặc “Không gửi được” kèm mã HTTP (hoặc “không có phản hồi”), thời lượng và phần đầu của phản hồi
- “URL đích (https://…)”
- Tối đa 2048 ký tự. Chỉ https, không có thông tin đăng nhập trong URL, không có địa chỉ localhost và không có tên nội bộ như .local, .internal hay .lan. Tên phải trỏ đến địa chỉ IP công khai; LinkHarbor kiểm tra lại điều này trước mỗi lượt gửi.
- “Sự kiện”
- Ít nhất một sự kiện. Mỗi nhóm có “Chọn tất cả” và “Bỏ chọn tất cả”.
- “Mô tả”
- Không bắt buộc, tối đa 255 ký tự – ví dụ webhook dùng để làm gì.
- “Trạng thái”
- “Hoạt động” hoặc “Đã tắt”. Webhook đã tắt không nhận lượt gửi.
Sự kiện
| Sự kiện | Trong bảng điều khiển | Khi nào | Nội dung của data |
|---|---|---|---|
| page.published | Đã xuất bản trang | Một trang đã được xuất bản – trong bảng điều khiển, qua API hoặc bởi trợ lý AI | page_id, version, checksum, published_by_user_id (null với khóa API và trợ lý AI) |
| form.submitted | Biểu mẫu được gửi | Một khách truy cập đã gửi biểu mẫu trên một trang của bạn | form_id, form_name, submission_id, page_id, block_id, submitted_at, values (khóa trường → giá trị), consent (khóa đồng ý → true/false) |
| domain.verified | Đã xác minh tên miền | Một tên miền riêng đã được xác minh thành công | domain_id, hostname, verification_method, status |
| domain.disabled | Đã vô hiệu hóa tên miền | Một tên miền riêng đã bị vô hiệu hóa trong bảng điều khiển (không phải khi hạ gói) | domain_id, hostname, status |
| webhook.test | – (nút “Kiểm thử”) | Bạn nhấp “Kiểm thử” | message (một ghi chú theo ngôn ngữ tài khoản của người bắt đầu kiểm thử; đừng so khớp theo nó) |
form.submitted chứa các giá trị đúng như khách truy cập đã gửi, nhưng không bao giờ có địa chỉ IP hay trình duyệt của họ. Việc gửi chạy ở chế độ nền: khách truy cập không phải đợi, và một lượt gửi thất bại không ngăn được bài gửi.
Cấu trúc của một lượt gửi
LinkHarbor gửi mỗi lượt dưới dạng POST với phần thân JSON và các header sau:
| Header | Nội dung |
|---|---|
| Content-Type | application/json |
| X-LinkHarbor-Event | Tên sự kiện, ví dụ page.published |
| X-LinkHarbor-Delivery-Id | ID duy nhất của lượt gửi này (UUID), giống id trong phần thân |
| X-LinkHarbor-Timestamp | Thời điểm gửi tính bằng giây Unix |
| X-LinkHarbor-Signature | sha256= theo sau là chữ ký HMAC-SHA256 dạng hex |
| X-LinkHarbor-Signature-Version | v1 |
{
"id": "3f1c2a9e-8d4b-4a51-9f0e-6b7c1d2e3f40",
"event": "page.published",
"created_at": "2026-11-01T09:30:12+00:00",
"data": {
"page_id": 42,
"version": 8,
"checksum": "9b1e5c…",
"published_by_user_id": 7
}
} Lượt gửi lại nhận id mới cùng các trường redelivery_of (ID của lượt gửi được lặp lại) và delivery_root (ID của lượt gửi đầu tiên trong chuỗi này).
Xác minh chữ ký
Mỗi webhook có secret ký riêng (32 ký tự hex). Bạn chỉ thấy nó đúng một lần: ở bước cuối khi tạo webhook và ngay sau “Secret ↺”. Hãy sao chép ngay lên máy chủ – LinkHarbor không hiển thị lại sau này. LinkHarbor dùng nó để tính chữ ký như sau:
- Tạo thông điệp: timestamp, dấu chấm, delivery ID, dấu chấm, phần thân thô – tức timestamp.delivery_id.body
- Tính HMAC-SHA256 trên thông điệp này với secret ký làm khóa
- Gửi kết quả dưới dạng chuỗi hex với tiền tố sha256=
Máy chủ của bạn nên kiểm tra chữ ký bằng phép so sánh thời gian hằng định, từ chối lượt gửi có timestamp cũ hơn 300 giây và lưu delivery ID ít nhất chừng ấy thời gian để nhận ra lượt lặp lại. Quan trọng: phần thân thô được ký, đúng như khi nó đến – không phải JSON bạn đã mã hóa lại.
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const SECRET = process.env.LH_WEBHOOK_SECRET;
const seen = new Set(); // khi chạy thật: một cơ sở dữ liệu hoặc Redis có thời hạn
app.post('/webhooks/linkharbor', express.raw({ type: 'application/json' }), (req, res) => {
const ts = req.get('X-LinkHarbor-Timestamp') || '';
const id = req.get('X-LinkHarbor-Delivery-Id') || '';
const sig = req.get('X-LinkHarbor-Signature') || '';
if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300) {
return res.status(400).send('timestamp');
}
const expected = 'sha256=' + crypto
.createHmac('sha256', SECRET)
.update(ts + '.' + id + '.')
.update(req.body) // phần thân thô dưới dạng Buffer
.digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(sig);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return res.status(401).send('signature');
}
const event = JSON.parse(req.body.toString('utf8'));
const key = event.delivery_root || event.id; // nhận ra các lượt gửi lại trong cùng chuỗi
if (!seen.has(key)) {
seen.add(key);
// xử lý tại đây, tốt nhất là bất đồng bộ trong hàng đợi
}
res.sendStatus(200);
});
app.listen(3000); <?php
$secret = getenv('LH_WEBHOOK_SECRET');
$body = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_LINKHARBOR_TIMESTAMP'] ?? '';
$id = $_SERVER['HTTP_X_LINKHARBOR_DELIVERY_ID'] ?? '';
$sig = $_SERVER['HTTP_X_LINKHARBOR_SIGNATURE'] ?? '';
if (!ctype_digit($ts) || abs(time() - (int)$ts) > 300) {
http_response_code(400);
exit('timestamp');
}
$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $id . '.' . $body, $secret);
if (!hash_equals($expected, $sig)) {
http_response_code(401);
exit('signature');
}
$event = json_decode($body, true);
// Nhận ra lượt gửi trùng lặp qua $event['delivery_root'] ?? $event['id'],
// rồi xử lý.
http_response_code(200); Thử endpoint bằng curl
Script này gửi một lượt gửi thử được ký đúng đến máy chủ của bạn – tiện lợi trước khi bạn tạo webhook trong LinkHarbor:
SECRET="your-signing-secret"
URL="https://example.com/webhooks/linkharbor"
TS=$(date +%s)
ID=$(uuidgen | tr 'A-Z' 'a-z')
BODY='{"id":"'"$ID"'","event":"webhook.test","created_at":"2026-11-01T09:30:12+00:00","data":{"message":"Đây là sự kiện thử cho webhook này."}}'
SIG="sha256=$(printf '%s' "$TS.$ID.$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')"
curl -X POST "$URL" \
-H "Content-Type: application/json" \
-H "X-LinkHarbor-Event: webhook.test" \
-H "X-LinkHarbor-Delivery-Id: $ID" \
-H "X-LinkHarbor-Timestamp: $TS" \
-H "X-LinkHarbor-Signature: $SIG" \
-H "X-LinkHarbor-Signature-Version: v1" \
-d "$BODY" Phản hồi, thời gian chờ và gửi lại
- Mọi phản hồi có trạng thái 2xx đều được coi là thành công; chuyển hướng (3xx) không được theo
- Máy chủ của bạn có 10 giây để phản hồi, và tối đa 5 giây để thiết lập kết nối
- Mọi webhook của một sự kiện dùng chung tổng cộng 25 giây; những webhook chưa đến lượt khi hết thời gian bị đánh dấu thất bại và có thể được gửi lại
- LinkHarbor không tự động thử lại các lượt gửi thất bại
- Bạn có thể gửi lại thủ công các lượt gửi thất bại, nhưng không thể gửi lại lượt đã thành công sau đó. Lượt gửi lại có id mới và created_at mới
- Mỗi sự kiện được phép tối đa 3 lần thử, kể cả lượt gửi đầu tiên; sau đó nó nằm trong “Dead letter” và không thể gửi lại nữa
Lượt gửi trong bảng điều khiển
“Lượt gửi” mở nhật ký của một webhook. Nhật ký có ba tab:
- “Tất cả lượt gửi”
- Mọi lượt gửi, với bộ lọc theo sự kiện, trạng thái (“Thành công (2xx)”, “Lỗi phía máy khách (4xx)”, “Lỗi phía máy chủ (5xx)”, “Thất bại”) và “Giới hạn”. “↺ Thử lại” gửi lại một lượt gửi thất bại, miễn là chưa có lượt thành công sau đó. Nhấp vào một dòng để xem yêu cầu, phản hồi, phần thân và JSON thô.
- “Gửi lại”
- Các chuỗi gửi lại với kết quả “Đã khôi phục” hoặc “Vẫn thất bại” và số lần thử.
- “Dead letter”
- Các lượt gửi mà mọi lần thử được phép đều thất bại – lượt gửi đầu tiên và các lượt gửi lại cộng lại. Tab chỉ để xem: chúng không thể được gửi lại nữa. Hãy kiểm tra endpoint của bạn; sau đó các sự kiện mới được gửi như bình thường. Nếu cần dữ liệu, hãy lấy qua API.
Nếu một lượt gửi lại không thành công, bảng điều khiển nêu lý do:
- “Không gửi lại: sự kiện này đã thất bại … lần, không thể thử thêm. Bạn sẽ thấy nó trong ‘Dead letter’.”
- Cả 3 lần thử đã được dùng hết.
- “Không gửi lại: sự kiện này đã được gửi thành công trước đó.”
- Một lượt gửi sau trong cùng chuỗi đã thành công.
Xác minh trong bảng điều khiển
Trong “Xác minh”, bạn có hai công cụ hỗ trợ:
- “Bộ xác minh”
- “Tải bộ xác minh” hiển thị thuật toán, tên các header, định dạng thông điệp được ký và độ dung sai khuyến nghị 300 giây. Bản thân secret không được bao gồm.
- “Xác minh chữ ký”
- Nhập delivery ID, timestamp, chữ ký và phần thân yêu cầu thô của một lượt gửi bạn đã nhận. LinkHarbor sẽ cho bạn biết “Chữ ký hợp lệ” hoặc “Chữ ký không hợp lệ”. Sao chép timestamp và chữ ký từ các header X-LinkHarbor-Timestamp và X-LinkHarbor-Signature. Việc kiểm tra chỉ hoạt động với lượt gửi trong 5 phút gần nhất; với lượt cũ hơn, kết quả là “Chữ ký không hợp lệ” dù chữ ký đúng.
Xoay vòng secret, tắt, xóa
- “Secret ↺” tạo secret ký mới và hiển thị một lần; secret cũ ngừng hoạt động ngay, và máy chủ của bạn phải dùng secret mới
- Trong “Chỉnh sửa”, đặt trạng thái thành “Đã tắt” để tạm dừng lượt gửi
- Biểu tượng thùng rác xóa webhook; cho đến khi bạn đóng thông báo, bạn có thể hủy bằng “Hoàn tác”
Khi chuyển sang gói nhỏ hơn, các webhook vượt giới hạn mới bị tắt, và ở Creator Free là tất cả. Chúng vẫn được giữ; sau khi nâng cấp, LinkHarbor tự động bật lại các webhook đã tắt đến giới hạn của gói mới – kể cả những webhook bạn tự tắt.
Có thể bạn cũng thấy hữu ích
Không tìm thấy?
Hãy cho chúng tôi biết bạn cần gì, đội hỗ trợ sẽ trực tiếp giúp bạn.