# Webhook và sự kiện

> Luồng sự kiện X-Buddy, gửi webhook và thử lại, xác minh chữ ký, quy tắc URL và gửi thử đến endpoint.

Source: https://x.builera.app/docs/vi/webhooks-and-events
Last checked: 2026-10-24

X-Buddy ghi lại những gì diễn ra trong workspace dưới dạng **sự kiện**. Bạn có thể đọc bằng cách thăm dò, hoặc nhận dưới dạng **webhook** tại endpoint HTTPS của mình.

## Thăm dò {#polling}

`GET /events` (`events_list`) trả về các sự kiện theo thứ tự. Truyền `cursor` của sự kiện cuối cùng bạn đã xử lý vào `after` để chỉ lấy các sự kiện mới hơn. Con trỏ sự kiện là các số tăng dần (ở dạng chuỗi). Sự kiện được lưu trong **30 ngày**. Thăm dò là cách đáng tin cậy; webhook là cách nhận nhanh.

## Các loại sự kiện {#event-types}

| Nhóm | Loại |
|---|---|
| Việc | `job.succeeded`, `job.failed`, `job.unknown`, `job.held`, `job.cancelled`, `batch.completed` |
| Hộp thư và tương tác | `dm.received`, `dm.request_received`, `reply.received`, `mention.received`, `follower.gained`, `follower.lost` |
| Bài đăng | `post.published`, `post.failed`, `post.partial` |
| Chiến dịch | `campaign.person_entered`, `campaign.person_replied`, `campaign.person_completed`, `campaign.paused_auto` |
| Nick | `account.session_lost`, `account.session_restored`, `account.safety_changed`, `account.x_rate_limited`, `account.chat_locked` |
| Hạ tầng và các loại khác | `vps.offline`, `vps.online`, `proxy.failed`, `alert.opened`, `integration.paused`, `scan.completed`, `limit.reached` |

## Cấu trúc bao ngoài {#the-envelope}

Sự kiện tuân theo CloudEvents 1.0:

```json
{
  "specversion": "1.0",
  "id": "<event id>",
  "type": "job.succeeded",
  "source": "x-buddy:workspace:<workspace_id>",
  "subject": "<what the event is about>",
  "time": "2026-11-01T09:00:00Z",
  "observed_at": "2026-11-01T09:00:01Z",
  "cursor": "1042",
  "workspace_id": "<workspace_id>",
  "account_id": "<account_id>",
  "job_id": "<job_id>",
  "integration_id": null,
  "data": {}
}
```

Khi kết nối không có quyền đọc văn bản tin nhắn, các sự kiện có nội dung sẽ chứa `"text_withheld": true` thay cho văn bản.

## Xác minh chữ ký {#verify-the-signature}

Các lần gửi tuân theo Standard Webhooks và chứa ba header: `webhook-id`, `webhook-timestamp` và `webhook-signature` (`v1,<base64>`).

1. Tạo chuỗi `<webhook-id>.<webhook-timestamp>.<raw body>`.
2. Tính HMAC-SHA256 bằng khoá bí mật ký của bạn (giải mã base64 sau khi bỏ tiền tố `whsec_`).
3. So sánh với từng chữ ký `v1` trong header (có thể có hai chữ ký trong thời gian luân chuyển khoá bí mật, với khoảng chồng lấp 24 giờ).
4. Từ chối dấu thời gian lệch quá 5 phút so với đồng hồ của bạn.

```ts
import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(secret: string, id: string, ts: string, body: string, header: string): boolean {
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
  const expected = createHmac("sha256", key).update(`${id}.${ts}.${body}`).digest();
  return header.split(" ").some((part) => {
    const [v, sig] = part.split(",");
    if (v !== "v1" || !sig) return false;
    const got = Buffer.from(sig, "base64");
    return got.length === expected.length && timingSafeEqual(got, expected);
  });
}
```

## Gửi và thử lại {#delivery-and-retries}

- Gửi bằng `POST` với thời gian chờ tối đa 10 giây. Chỉ phản hồi `2xx` mới được tính là gửi thành công; không đi theo chuyển hướng.
- Thử lại sau 30 giây, 2 phút, 10 phút, 1 giờ, 6 giờ, 12 giờ, 24 giờ và 24 giờ (dao động ngẫu nhiên ±20%), trong tối đa 72 giờ.
- Sau **20 lần thất bại liên tiếp trong 24 giờ**, endpoint bị tạm dừng và một cảnh báo được mở.

Dùng `id` của sự kiện để bỏ qua các bản trùng lặp: lần gửi thử lại có cùng id.

## Quy tắc URL {#url-rules}

Endpoint phải dùng `https://`, trên cổng mặc định, 443 hoặc 8443. Địa chỉ chứa thông tin xác thực và các địa chỉ nội bộ, loopback, link-local, riêng tư hoặc cloud-metadata đều bị từ chối — kể cả tên miền phân giải về các địa chỉ đó. X-Buddy kiểm tra lại ngay trước khi gửi.

## Kiểm tra endpoint {#test-an-endpoint}

`POST /webhooks/{endpoint_id}/test` (`webhook_test`) gửi ngay một sự kiện `webhook.test` có chữ ký và trả về trạng thái HTTP, thời gian thực hiện cùng phần đầu phản hồi của bạn. Thao tác này không làm gì trên X.

## Liên quan {#related}

- [REST API](/docs/vi/rest-api)
- [Cảnh báo và Telegram](/docs/vi/alerts-and-telegram)
