Menu

Webhook

Kabari sistem kamu otomatis setiap ada pesanan, pendaftaran, pembatalan, atau check-in.

Dengan webhook, Nyebar mengirim kabar ke sistem kamu begitu sesuatu terjadi, misalnya untuk memasukkan pembeli ke CRM, mengirim pesan WhatsApp sambutan dari sistem sendiri, atau memperbarui papan skor kehadiran. Sistem kamu tidak perlu bertanya terus ke API.

Webhook diatur oleh owner atau Admin di menu Integrasi. Bagian akhir artikel ini ditujukan untuk developer yang menyiapkan penerimanya.

Kejadian yang bisa dikirim

KejadianKapan dikirim
Pesanan terkonfirmasi (order.confirmed)Pesanan dibayar (atau gratis) dan tiketnya terbit. Termasuk pendaftaran gratis yang kamu setujui.
Pendaftaran masuk (registration.received)Pendaftaran baru menunggu persetujuan kamu.
Pesanan batal (order.cancelled)Pendaftaran ditolak, atau pesanan kedaluwarsa atau gagal dibayar.
Tiket check-in (ticket.checked_in)Tiket di-check-in, dari dasbor, link kru, maupun API.

Menambah webhook

  1. Buka menu Integrasi, lalu di bagian Webhook klik Tambah webhook.

    Daftar webhook di halaman Integrasi.
  2. Isi URL endpoint, yaitu alamat di sistem kamu yang menerima kiriman. Alamatnya harus https dan bisa diakses dari internet.

  3. Pilih Kejadian yang dikirim, lalu klik Simpan.

    Isi URL dan pilih kejadian yang ingin dikirim.
  4. Salin secret yang muncul dan pasang di sistem kamu untuk memverifikasi kiriman. Secret juga bisa dilihat lagi di halaman webhook.

Satu organizer bisa punya sampai 5 webhook.

Memantau kiriman

Di daftar webhook, setiap baris punya dua ikon:

  • Riwayat kiriman (ikon jam): kiriman 30 hari terakhir. Klik satu kiriman untuk melihat isi kiriman dan respons dari sistem kamu, lalu Kirim ulang kalau perlu.
  • Pengaturan (ikon roda gigi): mengganti URL, deskripsi, atau kejadian lewat Edit Endpoint, menyalakan atau mematikan webhook sementara, melihat atau mengganti Secret penandatanganan, dan menghapus webhook.

Di bagian atas kedua halaman ada Kirim tes, untuk memastikan endpoint kamu menerima kiriman dengan benar.

Riwayat kiriman sebuah webhook.

Kalau sistem kamu tidak menjawab dengan sukses, Nyebar mencoba lagi otomatis setelah 1 menit, 5 menit, 30 menit, 2 jam, 6 jam, dan 24 jam. Kalau kiriman ke satu webhook terus gagal selama lebih dari sehari, webhook dimatikan otomatis dan owner serta Admin mendapat notifikasi di dasbor. Setelah endpoint diperbaiki, kirim tes lalu nyalakan lagi. Kejadian yang terjadi selama webhook mati tetap dikirim setelah dinyalakan, selama belum lewat 30 hari.

Untuk developer

Format kiriman

Nyebar mengirim POST berisi JSON ke URL kamu, dengan header:

HeaderIsi
Nyebar-Event-IdID kejadian, misalnya evt_1042. Sama untuk setiap percobaan ulang.
Nyebar-Event-TypeJenis kejadian, misalnya order.confirmed.
Nyebar-SignatureTanda tangan, t=<waktu unix>,v1=<hex>.
{
  "id": "evt_1042",
  "type": "order.confirmed",
  "created_at": "2026-10-11T14:36:46Z",
  "data": {
    "event": { "id": 134, "title": "Nusantara City Run 10K", "slug": "nusantara-city-run-10k" },
    "order": {
      "id": 1230,
      "code": "ORDRHMRQERV",
      "status": "confirmed",
      "buyer_name": "Andi Nasution",
      "buyer_email": "andi@contoh.com",
      "ticket_subtotal": 200000,
      "addon_subtotal": 0,
      "discount_amount": 0,
      "platform_fee_amount": 0,
      "total_price": 200000,
      "created_at": "2026-10-11T14:30:02Z"
    },
    "tickets": [
      {
        "id": 1583,
        "code": "NYE8KRXGKU8",
        "holder_name": "Andi Nasution",
        "holder_email": "andi@contoh.com",
        "ticket_type": { "id": 235, "name": "Reguler" },
        "bib_number": null,
        "checked_in_at": null,
        "created_at": "2026-10-11T14:36:46Z"
      }
    ]
  }
}

Isi data per kejadian:

  • order.confirmed: event, order, dan tickets yang terbit.
  • registration.received: event dan order (status awaiting_approval).
  • order.cancelled: event, order, dan reason: rejected (ditolak), expired (batas waktu persetujuan atau pembayaran lewat), payment_failed, atau payment_expired.
  • ticket.checked_in: event, ticket, dan source: dashboard, crew_link, api, atau support.

Bentuk order dan ticket sama dengan respons API organizer.

Menjawab kiriman

Jawab dengan status 2xx dalam 10 detik. Kalau sistem kamu butuh waktu lebih lama, simpan kirimannya dulu, jawab 200, lalu proses belakangan. Status selain 2xx, timeout, atau redirect dianggap gagal dan dicoba lagi.

Kiriman bisa datang lebih dari sekali (misalnya saat percobaan ulang) dan urutannya tidak dijamin. Gunakan id kejadian untuk melewati kiriman yang sudah pernah diproses, dan created_at untuk urutannya.

Memverifikasi tanda tangan

Hitung HMAC-SHA256 dari <t>.<body mentah> dengan secret webhook, lalu bandingkan dengan nilai v1. Tolak juga kiriman yang t-nya lebih dari 5 menit dari waktu sekarang.

// Node.js (Express): pakai body mentah, bukan hasil JSON.parse
const crypto = require("crypto");

app.post("/webhooks/nyebar", express.raw({ type: "application/json" }), (req, res) => {
  const header = req.get("Nyebar-Signature") || "";
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = crypto
    .createHmac("sha256", process.env.NYEBAR_WEBHOOK_SECRET)
    .update(`${parts.t}.${req.body}`)
    .digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  if (!fresh || !parts.v1 || !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))) {
    return res.sendStatus(400);
  }
  const event = JSON.parse(req.body);
  // ... proses event ...
  res.sendStatus(200);
});
<?php
// PHP
$body = file_get_contents('php://input');
parse_str(str_replace(',', '&', $_SERVER['HTTP_NYEBAR_SIGNATURE'] ?? ''), $parts);
$expected = hash_hmac('sha256', ($parts['t'] ?? '') . '.' . $body, getenv('NYEBAR_WEBHOOK_SECRET'));
if (abs(time() - (int) ($parts['t'] ?? 0)) > 300 || !hash_equals($expected, $parts['v1'] ?? '')) {
    http_response_code(400);
    exit;
}
$event = json_decode($body, true);
// ... proses event ...
http_response_code(200);
# Python (Flask)
import hashlib, hmac, os, time
from flask import Flask, request, abort

app = Flask(__name__)

@app.post("/webhooks/nyebar")
def nyebar_webhook():
    parts = dict(p.split("=", 1) for p in request.headers.get("Nyebar-Signature", "").split(",") if "=" in p)
    body = request.get_data()
    expected = hmac.new(os.environ["NYEBAR_WEBHOOK_SECRET"].encode(), parts.get("t", "").encode() + b"." + body, hashlib.sha256).hexdigest()
    if abs(time.time() - int(parts.get("t", 0))) > 300 or not hmac.compare_digest(expected, parts.get("v1", "")):
        abort(400)
    event = request.get_json()
    # ... proses event ...
    return "", 200

Kalau secret diganti lewat Ganti secret, secret lama langsung tidak berlaku. Perbarui secret di sistem kamu segera setelahnya.

Apakah artikel ini membantu?

Masih butuh bantuan?

Tim Nyebar siap membantu lewat WhatsApp atau email. Cara lain menghubungi kami

Chat WhatsApp

Artikel terkait

Ketik untuk mencari artikel bantuan…