# Terima event Klik Flow di sistem Anda.

Tambahkan URL HTTPS di Pengaturan › API & Webhook › Webhook (atau lewat `POST /api/v2/webhooks`) dan pilih event-nya. Klik Flow mengirim POST JSON yang ditandatangani dengan secret webhook itu. Daftar event dan isinya ada di panduan Event webhook.

Sumber: https://klikflow.id/docs/api/webhook

## Bentuk kiriman

Setiap event dikirim dengan bentuk yang sama:

```
{
  "id": "evt_9Qx2Lm4Kp7Rt1Zs8",
  "type": "message.status",
  "created": "2026-11-01T09:13:10+07:00",
  "business_id": 7,
  "data": {
    "message_id": 99140,
    "conversation_id": 812,
    "channel": "whatsapp_official",
    "to": "6281234567890",
    "status": "read",
    "error": null,
    "at": "2026-11-01T09:13:10+07:00"
  }
}
```

- `id`: id unik event (`evt_…`), sama di setiap percobaan. Simpan, dan abaikan bila sudah pernah diproses.
- `created`: waktu kejadian dalam zona waktu bisnis Anda, lengkap dengan selisihnya (`+07:00`).
- `test: true` ada pada event uji dan pada status pesan yang dikirim kunci tes.
- Maksimal 5 webhook per bisnis; setiap webhook punya secret sendiri.

## Header

- `Klikflow-Event`: nama event, misalnya `message.received`.
- `Klikflow-Event-Id`: sama dengan `id` di isi.
- `Klikflow-Delivery-Attempt`: percobaan ke berapa (1–7).
- `Klikflow-Signature: t=<detik>,v1=<HMAC-SHA256 dari "<t>.<body>">`, dibuat ulang di setiap percobaan.

## Verifikasi tanda tangan (Node.js)

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

// rawBody: isi permintaan apa adanya (string), sebelum di-parse.
function verifyKlikFlow(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.trim().split("=")));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; // lebih dari 5 menit: tolak
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return parts.v1?.length === expected.length && crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
}
```

## Verifikasi tanda tangan (PHP)

```
<?php
$raw = file_get_contents("php://input");
$header = $_SERVER["HTTP_KLIKFLOW_SIGNATURE"] ?? "";
parse_str(str_replace(",", "&", $header), $parts);
$t = intval($parts["t"] ?? 0);
$expected = hash_hmac("sha256", $t . "." . $raw, getenv("KLIKFLOW_WEBHOOK_SECRET"));
if (!$t || abs(time() - $t) > 300 || !hash_equals($expected, $parts["v1"] ?? "")) {
    http_response_code(400);
    exit;
}
$event = json_decode($raw, true);
http_response_code(200);
```

## Verifikasi tanda tangan (Python)

```
import hashlib, hmac, time

def verify_klikflow(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.strip().split("=", 1) for p in header.split(",") if "=" in p)
    t = int(parts.get("t", "0"))
    if not t or abs(time.time() - t) > 300:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))
```

## Menjawab dan kirim ulang

Jawab 2xx dalam 8 detik, lalu proses di belakang. Bila tidak, kiriman diulang setelah 1 menit, 5 menit, 30 menit, 2 jam, 6 jam, 24 jam (7 percobaan, sekitar 33 jam). Setelah itu kiriman masuk daftar gagal.

- Klik Flow tidak mengikuti redirect: 3xx dihitung gagal, jadi pakai URL akhir.
- Hanya alamat HTTPS publik; alamat jaringan lokal ditolak.
- Kirim ulang atau Kirim ulang semua di Pengaturan › API & Webhook mengirim event yang sama (id dan isi sama) dengan percobaan baru.
- Bila semua kiriman gagal selama 3 hari, webhook dijeda otomatis dan alasannya tampil di kartunya. Aktifkan lagi setelah penerima diperbaiki; kiriman yang menunggu dicoba ulang saat itu juga.
- Log kiriman (status, kode HTTP, waktu, dan isi) disimpan 30 hari.

## Urutan dan duplikat

Event bisa tiba tidak berurutan, dan setelah gangguan jaringan bisa tiba dua kali. Pakai `id` untuk mengabaikan ulangan, dan waktu di `data` (misalnya `at` pada status pesan) untuk urutan.

## Event uji

Tombol Kirim event uji di kartu webhook, atau `POST /api/v2/webhooks/{id}/test`, mengirim contoh satu event dengan `test: true` dan langsung menampilkan jawaban sistem Anda. Event uji tidak diulang bila gagal dan tidak dihitung dalam kesehatan webhook.
