Jawaban singkat: Apa itu webhook? Ia adalah permintaan HTTP yang dikirim sebuah layanan ke URL milikmu setiap kali terjadi sesuatu, misalnya status paket berubah. Bedanya dengan polling, server kamu tidak perlu terus bertanya karena kabar datang sendiri. Agar aman, endpoint webhook perlu HTTPS, verifikasi tanda tangan HMAC, pengecekan timestamp, dan penanganan event duplikat.

Toko online yang mulai ramai biasanya punya satu rutinitas yang melelahkan: admin membuka halaman pelacakan satu per satu, lalu memperbarui status pesanan dan membalas pembeli. Kalau kamu developer toko atau pemilik toko yang punya tim IT kecil, pertanyaan apa itu webhook akan muncul begitu pekerjaan ini ingin dipindahkan ke server. Penjelasan berikut sengaja netral dan tidak terikat penyedia layanan tertentu.

Apa itu webhook?

Webhook adalah permintaan HTTP, umumnya dengan metode POST, yang dikirim penyedia layanan ke URL milik penerima saat ada kejadian tertentu. URL penerima itu disebut endpoint, isi datanya disebut payload, dan kejadiannya disebut event.

Analogi paling mudah: polling seperti bolak-balik ke depan rumah untuk mengecek kotak surat, padahal sering kosong. Webhook seperti bel pintu. Kamu baru bergerak saat ada yang datang, dan tamunya langsung membawa kabar.

Untuk pengiriman paket, “tamu” itu adalah perubahan status: paket diserahkan ke kurir, tiba di gudang transit, dibawa kurir antar, atau diterima pembeli.

Webhook vs polling: mana yang lebih cocok?

Untuk perubahan yang jarang tetapi penting, seperti status paket, webhook lebih hemat dan kabarnya datang lebih cepat. Polling tetap berguna sebagai cadangan dan untuk sistem yang tidak bisa menerima permintaan dari luar.

Tabel berikut membandingkan keduanya dari sisi yang paling sering ditanyakan developer.

Aspek Polling Webhook
Cara kerja Server kamu bertanya berkala, misalnya tiap 15 menit Penyedia mengirim kabar saat ada event
Kecepatan kabar Tergantung interval tanya Segera setelah event tercatat di penyedia
Beban Banyak permintaan sia-sia saat tidak ada perubahan Permintaan hanya saat ada event
Kerumitan Sederhana, cukup jadwal berkala Perlu endpoint publik, verifikasi, dan penanganan retry
Cocok untuk Volume kecil, sistem tertutup, rekonsiliasi Banyak resi aktif, notifikasi ke pembeli

Banyak tim memakai keduanya: webhook untuk kabar utama, dan polling yang jarang sebagai rekonsiliasi untuk menangkap event yang mungkin terlewat.

Bagaimana webhook dipakai untuk status pengiriman?

Alurnya dimulai dari pemindaian paket oleh kurir dan berakhir di status pesanan toko kamu. Secara umum urutannya seperti ini:

  1. Kurir memindai paket, misalnya saat paket tiba di gudang transit.
  2. Penyedia layanan pengiriman mencatat status baru dari data kurir.
  3. Penyedia mengirim POST berisi data event ke endpoint toko.
  4. Server toko memverifikasi tanda tangan, menyimpan event, lalu membalas dengan kode 2xx.
  5. Proses latar belakang memperbarui status pesanan dan, bila perlu, memicu email ke pembeli.

Status umumnya berubah saat kurir memindai paket, jadi webhook mengikuti ritme pemindaian, bukan pergerakan paket detik demi detik. Ritmenya sama dengan yang terlihat saat pembeli mengecek paket di halaman cek resi: diperbarui berkala, kadang diam lama di satu titik. Arti status yang tertahan di gudang dibahas di paket nyangkut di DC.

Webhook juga tidak menggantikan rutinitas gudang. Selama integrasi dibangun, SOP pengiriman harian tetap jadi pegangan tim yang mengemas dan menyerahkan paket.

Contoh payload webhook status pengiriman

Payload adalah isi data yang dikirim bersama webhook, umumnya dalam format JSON. Contoh di bawah generik dan karangan; nama field serta nilainya berbeda di tiap penyedia, jadi selalu ikuti dokumentasi penyedia yang kamu pakai.

{
  "id": "evt_contoh_7f3a9c",
  "event": "package.scanned",
  "occurred_at": "2026-09-21T09:15:00+07:00",
  "data": {
    "tracking_number": "CONTOH0001234567",
    "status": "in_transit",
    "description": "Paket tiba di gudang transit",
    "location": "Bandung"
  }
}

Empat bagian yang umumnya ada di payload event, meskipun nama field-nya berbeda di tiap penyedia:

  • id: penanda unik event. Ini kunci untuk menolak duplikat.
  • event: jenis kejadian, misalnya paket dipindai atau paket diterima.
  • occurred_at: waktu kejadian, sebaiknya lengkap dengan zona waktu.
  • data: detail kejadian, seperti nomor resi dan status baru.

Perhatikan occurred_at. Event bisa tiba tidak berurutan, jadi jangan menimpa status “diterima” dengan event “dalam perjalanan” yang datang terlambat. Bandingkan waktu kejadiannya sebelum memperbarui pesanan.

Cara mengamankan endpoint webhook

Anggap setiap permintaan ke endpoint webhook mencurigakan sampai terbukti berasal dari penyedia. Lapisan berikut umumnya cukup sebagai titik awal.

  • HTTPS saja. Jangan menerima webhook lewat HTTP biasa, karena payload dan tanda tangan bisa disadap di jalan.
  • Verifikasi tanda tangan HMAC atas raw body. HMAC, menurut RFC 2104, adalah cara mengotentikasi pesan dengan menggabungkan fungsi hash kriptografis dan kunci rahasia bersama. Praktik webhook modern umumnya memakai SHA-256. Hitung tanda tangan dari raw body persis seperti diterima, sebelum di-parse, karena perubahan spasi saja sudah membuat hasilnya berbeda.
  • Bandingkan dengan perbandingan waktu-konstan. Perbandingan string biasa bisa membocorkan informasi lewat perbedaan waktu proses.
  • Cek timestamp. Tolak permintaan yang timestamp-nya di luar toleransi, misalnya beberapa menit, untuk mencegah replay, yaitu permintaan lama yang dikirim ulang oleh pihak lain.
  • Simpan kunci rahasia di environment variable, bukan di kode, dan ganti bila ada tanda bocor.

Cara menyusun pesan yang ditandatangani, misalnya body saja atau gabungan timestamp dan body, serta nama header yang dipakai, berbeda di tiap penyedia. Contoh berikut generik untuk Node.js:

import { createHmac, timingSafeEqual } from "node:crypto";

// Contoh generik: susun signedMessage persis seperti dijelaskan dokumentasi penyedia.
export function isValidSignature(signedMessage: string, receivedHex: string, secret: string): boolean {
  const expectedHex = createHmac("sha256", secret).update(signedMessage, "utf8").digest("hex");
  const expected = Buffer.from(expectedHex, "hex");
  const received = Buffer.from(receivedHex, "hex");
  return expected.length === received.length && timingSafeEqual(expected, received);
}

// Contoh toleransi 120 detik; sesuaikan dengan kebutuhanmu.
export function isFresh(unixSeconds: number, toleranceSeconds = 120): boolean {
  return Math.abs(Date.now() / 1000 - unixSeconds) <= toleranceSeconds;
}

Terakhir, balas dengan kode 2xx secepat mungkin. Penyedia umumnya hanya menganggap kode kelas 2xx sebagai tanda berhasil, jadi simpan event, balas, lalu proses belakangan.

Menangani retry dan duplikasi

Webhook yang sama bisa datang lebih dari sekali, jadi pemrosesan event harus idempoten: diproses dua kali hasilnya sama dengan diproses sekali.

Menurut RFC 9110 bagian 9.2.2, metode POST tidak termasuk metode idempoten; yang idempoten adalah PUT, DELETE, dan metode aman seperti GET. Karena webhook dikirim lewat POST, penerima harus menolak duplikasi sendiri.

Duplikat biasanya muncul dari retry. Kalau penyedia tidak menerima balasan 2xx, misalnya karena timeout atau error 500, ia umumnya mencoba ulang dengan jeda yang makin panjang, memakai id event yang sama. Pola penanganannya:

  1. Simpan id event di tabel dengan constraint unik.
  2. Kalau id sudah ada, balas 2xx dan abaikan event itu.
  3. Masukkan event baru ke antrean, lalu proses di worker terpisah.
  4. Saat memproses, bandingkan occurred_at dengan status terakhir pesanan.
-- Contoh: tabel pencatat event yang sudah diterima
CREATE TABLE processed_webhook_events (
  event_id    text PRIMARY KEY,
  received_at timestamptz NOT NULL DEFAULT now()
);

-- Kalau event_id sudah ada, baris ini tidak menambah apa pun
INSERT INTO processed_webhook_events (event_id)
VALUES ('evt_contoh_7f3a9c')
ON CONFLICT (event_id) DO NOTHING;

Checklist sebelum menerima webhook

Sebelum endpoint webhook dipakai untuk pesanan sungguhan, pastikan lima hal ini sudah beres.

  • Endpoint hanya menerima POST lewat HTTPS.
  • Tanda tangan diverifikasi dari raw body dengan perbandingan waktu-konstan, dan kunci rahasia tersimpan di environment variable.
  • Timestamp dicek dengan toleransi beberapa menit.
  • Id event disimpan dengan constraint unik, dan event diproses di antrean setelah endpoint membalas 2xx.
  • Ada polling rekonsiliasi berkala serta log untuk event yang gagal diproses.