Docs menuWebhooks
API documentationBahasa Indonesia

Receive Klik Flow events in your system.

Add an HTTPS URL in Settings › API & Webhook › Webhook (Pengaturan › API & Webhook › Webhook), or with POST /api/v2/webhooks, and choose its events. Klik Flow sends a JSON POST signed with that webhook's secret. The list of events and their contents is in the Webhook events guide.

Delivery shape

Every event is sent in the same shape:

{
  "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: the event's unique id (evt_…), the same on every attempt. Store it, and ignore the event if it was already processed.
  • created: when it happened, in your business's time zone, with its offset (+07:00).
  • test: true is present on test events and on the status of messages sent with a test key.
  • At most 5 webhooks per business; each webhook has its own secret.

Headers

  • Klikflow-Event: the event name, for example message.received.
  • Klikflow-Event-Id: the same as id in the body.
  • Klikflow-Delivery-Attempt: the attempt number (1–7).
  • Klikflow-Signature: t=<seconds>,v1=<HMAC-SHA256 of "<t>.<body>">, generated again on every attempt.

Verify the signature (Node.js)

import crypto from "node:crypto";

// rawBody: the request body exactly as received (string), before parsing.
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; // more than 5 minutes off: reject
  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));
}

Verify the signature (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);

Verify the signature (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", ""))

Responding and retries

Respond with 2xx within 8 seconds, then do the processing in the background. Otherwise the delivery is retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, 24 hours (7 attempts, about 33 hours). After that, the delivery goes to the failed list.

  • Klik Flow does not follow redirects: a 3xx counts as a failure, so use the final URL.
  • Public HTTPS addresses only; local network addresses are refused.
  • Resend (Kirim ulang) or Resend all (Kirim ulang semua) in Settings › API & Webhook sends the same event (same id and content) as a new attempt.
  • If every delivery fails for 3 days, the webhook is paused automatically and the reason appears on its card. Reactivate it after the receiver is fixed; waiting deliveries are retried right away.
  • The delivery log (status, HTTP code, time, and content) is kept for 30 days.

Order and duplicates

Events can arrive out of order, and after a network problem they can arrive twice. Use id to ignore repeats, and the time in data (for example at in a message status) for ordering.

Test events

The Send test event button (Kirim event uji) on the webhook card, or POST /api/v2/webhooks/{id}/test, sends a sample of one event with test: true and shows your system's response right away. Test events are not retried when they fail and do not count toward the webhook's health.

Send WhatsApp from your system with the Klik Flow API.

Mulai dengan paket Free. Ajak tim setelah ruang kerja siap.