Bỏ qua để đến nội dung

Nhận webhook

Hướng dẫn thao tác
Dành cho:
Nhà phát triển
Cần quyền:
webhooks:manage
Trong ứng dụng:
Cài đặt, rồi Webhook

ReputeCast gửi sự kiện chuẩn hóa (thư gửi thành công, thư trả lại, khiếu nại, lượt đăng ký, lượt gửi và nhiều loại khác) tới các endpoint bạn đăng ký, và ký mọi yêu cầu. Trang này hướng dẫn thiết lập một endpoint và cách xác minh chữ ký webhook bằng Node.js, Python, Go và PHP, để mã của bạn chỉ xử lý những yêu cầu thực sự đến từ hệ thống ReputeCast của bạn.

  • Bạn cần quyền webhooks:manage trong không gian làm việc.
  • Endpoint của bạn cần một URL mà máy chủ ReputeCast truy cập được, chẳng hạn https://api.example.com/webhooks/reputecast. Địa chỉ trên mạng riêng và mạng nội bộ (10.0.0.0/8, localhost, *.internal và tương tự) bị từ chối, trừ khi quản trị viên hệ thống bật webhooks.allow_private_networks (tham chiếu cấu hình). URL chứa thông tin đăng nhập (https://user:pass@…) cũng bị từ chối; khóa bí mật ký webhook thay cho chúng.
  • Webhook thuộc về một không gian làm việc. Hãy thêm endpoint trong từng không gian làm việc mà bạn muốn nhận sự kiện.
  1. Mở Cài đặt, rồi Webhook và chọn Thêm endpoint.

  2. Nhập URL của endpoint, tên và mô tả (không bắt buộc), rồi chọn các sự kiện. Khi đánh dấu cả một nhóm, endpoint đăng ký ký tự đại diện của nhóm đó, chẳng hạn email.*; những sự kiện được thêm vào nhóm sau này cũng được bao gồm.

  3. Chọn Thêm endpoint. ReputeCast hiển thị khóa bí mật ký webhook, whsec_…, đúng một lần. Hãy sao chép nó vào cấu hình của bộ nhận, ví dụ dưới dạng REPUTECAST_WEBHOOK_SECRET. ReputeCast lưu khóa ở dạng mã hóa và không bao giờ hiển thị lại.

  4. Triển khai một bộ nhận có xác minh chữ ký (Xác minh chữ ký) và trả lời 2xx.

  5. Mở lại endpoint và chọn Gửi sự kiện thử. ReputeCast gửi ngay một sự kiện webhook.test và hiển thị phản hồi.

Cài đặt, thẻ Webhook: bốn endpoint webhook gửi đi cùng các sự kiện, tỷ lệ thành công trong 24 giờ và trạng thái của chúng. Một endpoint kho dữ liệu đăng ký email.* đang lỗi với HTTP 502, và một endpoint n8n được đánh dấu Managed by n8n. Bên dưới là thẻ Verify a signature.

Bạn cũng có thể quản lý các endpoint này qua API: create-webhook, update-webhook, pause-webhook, test-webhook và các thao tác khác trong nhóm Webhooks của tài liệu API.

Một endpoint đăng ký từ 1 đến 40 mục. Mỗi mục là một tên sự kiện (email.bounced), một ký tự đại diện cho cả nhóm (email.*) hoặc * cho mọi sự kiện. Danh mục đầy đủ, với ý nghĩa của từng sự kiện và mẫu data của nó, nằm trong Sự kiện webhook.

webhook.test không có trong danh mục: chỉ nút gửi thử và thao tác test-webhook mới gửi sự kiện này.

Mỗi lần gửi là một yêu cầu HTTP POST với phần thân JSON dạng envelope:

Phần thân yêu cầu
{
"id": "0192f1c4-7b2e-7c51-9d0a-3f5e8b1a2c44",
"type": "contact.subscribed",
"created_at": "2026-10-22T09:41:07Z",
"workspace": "acme-newsletter",
"data": { "…": "the event's own fields" }
}
  • id định danh sự kiện. Giá trị này giữ nguyên ở mọi lần thử lại và với mọi endpoint nhận sự kiện, nên hãy dùng nó để bỏ qua bản trùng lặp.
  • type là tên sự kiện, created_at là thời điểm sự kiện xảy ra (UTC) và workspace là slug của không gian làm việc.
  • data tùy thuộc vào từng sự kiện; xem Sự kiện webhook.

Mọi lần gửi đều kèm các header sau:

Header Giá trị
Content-Type application/json
User-Agent ReputeCast-Webhooks/<version>
X-ReputeCast-Signature HMAC-SHA256 của <timestamp>.<raw body>, mã hóa hex chữ thường. Không có tiền tố sha256= hay tiền tố nào khác.
X-ReputeCast-Timestamp Thời điểm ký lần thử này, tính bằng giây Unix.
X-ReputeCast-Event Tên sự kiện, giống type.
X-ReputeCast-Delivery ID của lần gửi: một ID cho mỗi cặp sự kiện và endpoint, giữ nguyên ở mọi lần thử lại.

Để kiểm tra một lần gửi:

  1. Đọc phần thân thô của yêu cầu dưới dạng byte, trước khi phân tích JSON. JSON đã được tuần tự hóa lại sẽ không khớp.
  2. Lấy header X-ReputeCast-Timestamp và từ chối yêu cầu khi giá trị này lệch quá 5 phút so với đồng hồ của bạn. Cách này ngăn một yêu cầu cũ bị bắt được rồi phát lại. ReputeCast không thực thi bước này; bộ nhận của bạn làm việc đó.
  3. Tính HMAC-SHA256 trên dấu thời gian, một dấu chấm và phần thân thô: <timestamp>.<raw body>.
  4. Dùng toàn bộ chuỗi khóa bí mật làm khóa, gồm cả tiền tố whsec_, dưới dạng byte UTF-8. Đừng bỏ tiền tố và đừng giải mã base64 phần còn lại.
  5. Mã hóa kết quả sang hex chữ thường và so sánh với X-ReputeCast-Signature theo thời gian hằng định (constant time). Trả lời 401 nếu không khớp.

Mỗi ví dụ dưới đây là một bộ nhận hoàn chỉnh trên cổng 3000 tại /webhooks/reputecast, đọc khóa bí mật từ REPUTECAST_WEBHOOK_SECRET:

server.mjs
import { createHmac, timingSafeEqual } from 'node:crypto'
import { createServer } from 'node:http'
// The HMAC key is the whole secret, whsec_ prefix included.
const secret = process.env.REPUTECAST_WEBHOOK_SECRET
function verify(rawBody, timestamp, signature) {
const age = Math.abs(Date.now() / 1000 - Number(timestamp))
if (!signature || !(age <= 300)) return false // reject timestamps more than 5 minutes off
const expected = createHmac('sha256', secret).update(`${timestamp}.`).update(rawBody).digest('hex')
const a = Buffer.from(expected)
const b = Buffer.from(signature)
return a.length === b.length && timingSafeEqual(a, b)
}
createServer((req, res) => {
if (req.method !== 'POST' || req.url !== '/webhooks/reputecast') return res.writeHead(404).end()
const chunks = []
req.on('data', (chunk) => chunks.push(chunk))
req.on('end', () => {
const rawBody = Buffer.concat(chunks) // the exact bytes ReputeCast signed
if (!verify(rawBody, req.headers['x-reputecast-timestamp'], req.headers['x-reputecast-signature'])) {
return res.writeHead(401).end()
}
const event = JSON.parse(rawBody.toString('utf8'))
console.log(event.type, event.id)
res.writeHead(204).end()
})
}).listen(3000)

Chạy trên Node.js 18 trở lên, không cần cài gói nào: node server.mjs.

  • Trong ReputeCast, Gửi sự kiện thử cho biết sự kiện thử đã được gửi tới chưa và bộ nhận của bạn đã trả lời gì. Nhật ký gửi trên cùng trang liệt kê mọi lần gửi kèm mã phản hồi.

  • Để thử bộ nhận mà không cần ReputeCast, hãy tự ký một yêu cầu bằng OpenSSL. Lệnh này tính ra đúng chữ ký đó:

    Terminal
    ts=$(date +%s)
    body='{"id":"0192f1c4-7b2e-7c51-9d0a-3f5e8b1a2c44","type":"webhook.test","created_at":"2026-10-22T09:41:07Z","workspace":"acme-newsletter","data":{}}'
    sig=$(printf '%s.%s' "$ts" "$body" | openssl dgst -sha256 -hmac "$REPUTECAST_WEBHOOK_SECRET" | sed 's/^.* //')
    curl -i -X POST http://localhost:3000/webhooks/reputecast \
    -H 'Content-Type: application/json' \
    -H "X-ReputeCast-Timestamp: $ts" \
    -H "X-ReputeCast-Signature: $sig" \
    --data "$body"

    Bộ nhận của bạn trả lời 204. Nếu đổi một ký tự trong body sau khi ký, nó trả lời 401.

Trả lời nhanh; ReputeCast lo phần thử lại

Phần tiêu đề “Trả lời nhanh; ReputeCast lo phần thử lại”
  • Mọi phản hồi 2xx đều được tính là đã gửi thành công. Hãy trả lời trong vòng 10 giây và làm phần việc chậm sau khi trả lời (ví dụ: đưa sự kiện vào hàng đợi của riêng bạn). Chuyển hướng (redirect) không được theo.
  • Mọi phản hồi khác, hết thời gian chờ hoặc lỗi kết nối đều là một lần thử thất bại. ReputeCast thử mỗi lần gửi tối đa 8 lần, chờ 1, 2, 4, 8, 30, 90 và 225 phút giữa các lần thử: tổng cộng khoảng 6 giờ.
  • Sau lần thất bại thứ 8, lần gửi chuyển vào hàng đợi dead-letter và nằm ở đó 7 ngày kể từ lần thử cuối. Để gửi lại, chọn Phát lại trong Nhật ký gửi: thao tác này bắt đầu một vòng 8 lần thử mới.
  • Để bỏ qua thời gian chờ, chọn Thử lại ngay trên một lần gửi đang thử lại, hoặc Thử lại mọi lần gửi lỗi để xếp hàng lại mọi lần gửi thất bại (retry-failed-webhooks trong API).
  • Lần thử lại và lần gửi lại mang cùng id và X-ReputeCast-Delivery, với dấu thời gian và chữ ký mới. Hãy làm cho trình xử lý của bạn có tính idempotent: bỏ qua id mà bạn đã xử lý.
  • Khi một endpoint đang lỗi, ReputeCast chỉ thử lần gửi chờ lâu nhất của nó, theo lịch ở trên. Các lần gửi khác chờ và được gửi đi theo thứ tự ngay khi endpoint trả lời 2xx trở lại. Không phần nào khác của ReputeCast phải chờ endpoint: việc gửi thư, nhập dữ liệu và tự động hóa vẫn tiếp tục.

Tạm dừng endpoint giữ lại các sự kiện mới của endpoint thay vì gửi đi, ví dụ trong lúc bảo trì. Tiếp tục endpoint gửi toàn bộ sự kiện đã giữ, theo đúng thứ tự.

Xoay vòng khóa bí mật để ký tạo khóa bí mật mới, hiển thị nó một lần và dùng nó để ký mọi lần gửi kể từ thời điểm đó. Khóa cũ ngừng hoạt động ngay; không có khoảng thời gian chồng lấn. Những lần gửi thất bại trong lúc bạn cập nhật bộ nhận sẽ được thử lại theo lịch ở trên (1, 2, 4 phút, v.v.), vì vậy hãy cập nhật bộ nhận ngay sau khi xoay vòng. Chỉ người dùng mới xoay vòng được khóa bí mật; khóa API thì không.

Zapier, Make và n8n tự tạo endpoint khi một quy trình (workflow) bắt đầu bằng trình kích hoạt ReputeCast. Cài đặt, rồi Webhook đánh dấu các endpoint này là do ứng dụng đó quản lý và chỉ cho xem; khóa API không thấy chúng. Khi một endpoint như vậy trả lời 410 Gone, ReputeCast xóa nó. Xem Xây dựng tích hợp.

Thiết lậpMặc địnhCách thay đổi
Số lần thử gửi8, with waits of 1, 2, 4, 8, 30, 90 and 225 minutes (about 6 hours)Cố định
Thời gian cho mỗi lần thử10 s; redirects are not followedCố định
Độ lệch dấu thời gian nên cho phép ở phía nhận5 minutesMã nguồn bên nhận webhook của bạn
Thời gian giữ lần gửi thất bại để phát lại7 days after the last attemptPhát lại bắt đầu một lượt mới gồm 8 lần thử
Thời gian giữ lần gửi thành công trong nhật ký30 daysCố định
Giới hạn của endpointURL up to 500 characters; 1–40 eventsCài đặt › Webhook
  • Mọi lần gửi đều bị bộ nhận trả lời 401: bạn đang băm JSON đã phân tích thay vì phần thân thô, dùng khóa bí mật mà bỏ tiền tố whsec_, hoặc so sánh với chuỗi hex chữ hoa. Hãy thử bằng lệnh OpenSSL ở trên.
  • Chỉ một số lần gửi bị 401: đồng hồ máy chủ của bạn bị lệch. Hãy đồng bộ bằng NTP; phép kiểm tra 5 phút so sánh với đồng hồ này.
  • Lỗi “… is a local hostname” hoặc địa chỉ bị chặn khi lưu: URL trỏ tới mạng riêng. Hãy dùng địa chỉ công khai, hoặc nhờ quản trị viên hệ thống đặt webhooks.allow_private_networks.
  • Lần gửi bị hết thời gian chờ: trình xử lý của bạn mất hơn 10 giây. Hãy trả lời trước, rồi mới xử lý.
  • Sự kiện thử hoạt động nhưng sự kiện thật không bao giờ đến: kiểm tra xem endpoint đã đăng ký những sự kiện đó chưa, và endpoint có đang bị tạm dừng không.
  • Sự kiện đến hai lần: một lần thử lại đã diễn ra sau một phản hồi đến quá muộn hoặc không phải 2xx. Hãy bỏ qua id mà bạn đã xử lý.