# Verifikasi Webhook

Setiap [outgoing webhook](/panduan/admin/outgoing-webhooks) di-tanda-tangani
dengan **HMAC-SHA256** atas body JSON mentah, hex-encoded, pada header
`X-Omnistream-Signature`. Selalu verifikasi signature **sebelum** mem-parse body.

## Dengan SDK

```ts
import { verifyWebhookSignature } from "@omnistreams/sdk";

// rawBody harus body mentah persis seperti diterima — verifikasi SEBELUM JSON.parse.
const ok = await verifyWebhookSignature(
  rawBody,
  req.headers["x-omnistream-signature"],
  webhookSecret,
);
if (!ok) return res.status(401).end();
```

### Rotasi secret

Selama rotasi secret, gateway ikut mengirim `X-Omnistream-Signature-Previous`
(ditandatangani secret lama). Terima salah satu:

```ts
import { verifyWebhookSignatureWithRotation } from "@omnistreams/sdk";

const ok = await verifyWebhookSignatureWithRotation(rawBody, webhookSecret, {
  current: req.headers["x-omnistream-signature"],
  previous: req.headers["x-omnistream-signature-previous"],
});
```

## Tanpa SDK (Node.js)

```js
import crypto from "node:crypto";

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

:::warning
Gunakan **raw body** (bytes asli). Men-serialize ulang objek yang sudah di-parse
dapat mengubah spasi/urutan kunci sehingga signature tidak cocok.
:::
