> ## Documentation Index
> Fetch the complete documentation index at: https://docs.madiad.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Nhận callback được ký khi bài đăng được xuất bản hoặc thất bại.

Webhooks đẩy sự kiện đến server của bạn nên không phải kiểm tra định kỳ. Đây là cách đáng tin cậy để biết kết quả của các bài đăng đã lên lịch và các lần tải lên bất đồng bộ (video).

## Đăng ký

Tạo một webhook endpoint trong dashboard tại [hub.madiad.com/dashboard/webhooks](https://hub.madiad.com/dashboard/webhooks): nhập URL sẽ nhận các lần phân phối và chọn các loại sự kiện bạn muốn theo dõi. Dashboard sẽ sinh một `secret` cho endpoint đó, hãy lưu lại. Bạn sẽ dùng giá trị này để xác minh mỗi lần phân phối.

## Sự kiện

| Sự kiện                      | Kích hoạt khi                                                                          |
| ---------------------------- | -------------------------------------------------------------------------------------- |
| `post.completed`             | Một lần tải lên đã xử lý xong trên nền tảng: thành công hay thất bại nằm trong payload |
| `connection.connected`       | Một tài khoản mạng xã hội vừa được kết nối vào Profile                                 |
| `connection.disconnected`    | Một tài khoản mạng xã hội vừa bị ngắt khỏi Profile                                     |
| `connection.reauth_required` | Một tài khoản đã kết nối cần được cấp quyền lại                                        |

Mỗi lần phân phối là một JSON body dạng `{ "id", "type", "created_at", "data": { … } }`. Với `post.completed`, `data` chứa `profile_id`, `platform`, `media_type`, `success`, `url`, `publish_id` và `error`.

## Xác minh chữ ký

Mỗi request đều mang header `X-MADIAD-Signature` ở dạng `sha256=<hex>`: một HMAC-SHA256 của **raw request body**, được ký bằng `secret` đăng ký của bạn. Hãy tính lại và so sánh trước khi tin tưởng payload. Mỗi lần phân phối cũng kèm header `X-MADIAD-Event` và `X-MADIAD-Delivery`.

```js theme={null}
import crypto from "node:crypto";

function verify(rawBody, signatureHeader, secret) {
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(signatureHeader);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
```

<Warning>
  Xác minh dựa trên body **thô, chưa qua parse**. Việc serialize lại JSON đã parse sẽ thay đổi bytes và chữ ký sẽ không khớp.
</Warning>

## Phản hồi và thử lại

* Trả về mã trạng thái `2xx` nhanh chóng (trong vài giây) để xác nhận đã nhận.
* Bất kỳ phản hồi nào không phải `2xx`, hoặc bị quá thời gian chờ, đều được thử lại với exponential backoff.
* Hãy làm cho trình xử lý của bạn **idempotent** vì một lần phân phối có thể đến nhiều hơn một lần. Khử trùng lặp theo `id` của lần phân phối (cũng được gửi qua header `X-MADIAD-Delivery`).

<Tip>
  Thực hiện các tác vụ chậm (ghi cơ sở dữ liệu, gọi dịch vụ khác) *sau khi* bạn đã phản hồi `2xx` (ví dụ bằng cách đưa sự kiện vào hàng đợi) để không bao giờ vượt quá thời gian chờ phân phối.
</Tip>
