Webhook
Webhook memungkinkan Anda menerima notifikasi waktu nyata saat ada peristiwa yang terjadi di akun LabelGrid Anda. Gunakan webhook untuk mengotomatiskan alur kerja dan berintegrasi dengan sistem eksternal.
Untuk Pengembang: Anda juga dapat mengelola webhook secara terprogram melalui API. Lihat Dokumentasi API LabelGrid untuk endpoint dan contohnya.
Cara Kerja Webhook
Section titled “Cara Kerja Webhook”- Anda mengonfigurasi webhook - Tentukan URL dan peristiwa apa saja yang ingin didengarkan
- Sebuah peristiwa terjadi - Misalnya, sebuah rilis dikirimkan ke toko
- LabelGrid mengirim permintaan POST - Server Anda menerima data peristiwa tersebut
- Sistem Anda memprosesnya - Otomatiskan alur kerja berdasarkan peristiwa tersebut
Mengakses Webhook
Section titled “Mengakses Webhook”- Klik ikon profil Anda di pojok kanan atas
- Pilih Webhooks dari menu turun
Membuat Webhook
Section titled “Membuat Webhook”- Klik Create Webhook
- Masukkan Name untuk menandai webhook ini
- Masukkan URL tempat Anda ingin menerima notifikasi
- Pilih Events mana yang akan memicu webhook ini
- Klik Create
Secret Webhook
Section titled “Secret Webhook”Saat membuat webhook, Anda akan menerima sebuah secret key. Gunakan ini untuk memverifikasi bahwa permintaan yang masuk benar-benar berasal dari LabelGrid:
- Simpan secret dengan aman
- Verifikasi signature pada permintaan yang masuk
- Jika bocor, buat ulang secret-nya
Peristiwa yang Tersedia
Section titled “Peristiwa yang Tersedia”Konfigurasikan webhook Anda untuk mendengarkan peristiwa berikut. Event Identifier adalah nilai yang akan Anda lihat pada properti event di payload dan pada header X-Webhook-Event:
| Event Identifier | Deskripsi |
|---|---|
delivery.completed | Dipicu saat sebuah rilis berhasil dikirimkan ke sebuah outlet |
delivery.failed | Dipicu saat pengiriman ke sebuah outlet gagal |
takedown.completed | Dipicu saat permintaan takedown selesai |
release.review.status_changed | Dipicu saat status peninjauan sebuah rilis berubah |
release.distributed | Dipicu saat sebuah rilis didistribusikan |
payment.statement_ready | Dipicu saat laporan pembayaran siap untuk dilihat |
Anda dapat memilih beberapa peristiwa untuk satu webhook, atau membuat webhook terpisah untuk jenis peristiwa yang berbeda.
Mengelola Webhook
Section titled “Mengelola Webhook”Melihat Webhook Anda
Section titled “Melihat Webhook Anda”Daftar webhook menampilkan:
| Kolom | Deskripsi |
|---|---|
| Name | Nama webhook yang Anda tetapkan |
| URL | Tempat notifikasi dikirim |
| Events | Jumlah peristiwa yang dikonfigurasi |
| Status | Active atau Inactive |
| Success / Fail | Jumlah pengiriman yang berhasil dan gagal |
| Last Triggered | Kapan webhook terakhir kali dipanggil |
Menyunting Webhook
Section titled “Menyunting Webhook”- Klik tindakan Edit pada baris webhook
- Ubah nama, URL, atau peristiwa
- Klik Save
Mengaktifkan / Menonaktifkan
Section titled “Mengaktifkan / Menonaktifkan”Alihkan status aktif webhook tanpa menghapusnya:
- Active - Webhook akan menerima notifikasi
- Inactive - Webhook dijeda, tidak ada notifikasi yang dikirim
Menghapus Webhook
Section titled “Menghapus Webhook”- Klik tindakan Delete pada baris webhook
- Konfirmasikan penghapusan
Menguji Webhook
Section titled “Menguji Webhook”Sebelum mengandalkan sebuah webhook di produksi, ujilah terlebih dahulu:
- Klik tindakan Test pada webhook Anda
- LabelGrid mengirimkan payload uji ke URL Anda
- Pastikan endpoint Anda menerima dan memprosesnya dengan benar
Melihat Log Webhook
Section titled “Melihat Log Webhook”Pantau aktivitas webhook dan atasi masalah:
- Klik tindakan View Logs pada sebuah webhook
- Lihat riwayat seluruh pengiriman webhook
Detail Log
Section titled “Detail Log”Setiap entri log menampilkan:
| Field | Deskripsi |
|---|---|
| Event Type | Peristiwa apa yang memicu pengiriman ini |
| Response Status | Kode status HTTP dari server Anda |
| Duration | Berapa lama permintaan tersebut berlangsung |
| Attempt | Nomor percobaan ulang |
| Timestamp | Kapan pengiriman terjadi |
Format Payload Webhook
Section titled “Format Payload Webhook”Saat sebuah peristiwa terjadi, LabelGrid mengirim permintaan POST ke URL Anda dengan payload JSON:
{ "event": "delivery.completed", "timestamp": "2026-05-05T10:00:00+00:00", "webhook_id": "123", "data": { // Event-specific data }}Field timestamp menggunakan format ISO 8601. webhook_id adalah ID webhook yang Anda konfigurasi (nilainya cocok dengan header X-Webhook-Id).
Payload Peristiwa
Section titled “Payload Peristiwa”Struktur objek data bergantung pada jenis event. Semua tipe field di bawah ini adalah tipe JSON sebagaimana diserialisasikan dalam payload.
delivery.completed
Section titled “delivery.completed”Dipicu satu kali per outlet saat pengiriman sebuah rilis mencapai status keberhasilan akhir.
{ "event": "delivery.completed", "timestamp": "2026-05-18T10:00:00+00:00", "webhook_id": "123", "data": { "distro_queue_id": 456, "release_id": 789, "release_cat": "ABC123", "outlet_id": 12, "outlet_name": "Spotify", "status": "complete" }}| Field | Tipe | Deskripsi |
|---|---|---|
distro_queue_id | integer | ID antrean internal untuk percobaan pengiriman ini |
release_id | integer | Rilis yang dikirimkan |
release_cat | string | null | Referensi katalog rilis Anda |
outlet_id | integer | ID outlet tujuan |
outlet_name | string | null | Nama outlet yang mudah dibaca (mis. "Spotify") |
status | string | Selalu "complete" untuk peristiwa ini |
delivery.failed
Section titled “delivery.failed”Dipicu satu kali per outlet saat pengiriman sebuah rilis mencapai status kegagalan akhir. Payload-nya sama dengan delivery.completed ditambah field message.
{ "event": "delivery.failed", "timestamp": "2026-05-18T10:00:00+00:00", "webhook_id": "123", "data": { "distro_queue_id": 456, "release_id": 789, "release_cat": "ABC123", "outlet_id": 12, "outlet_name": "Spotify", "status": "error", "message": "Outlet rejected the delivery: missing ISRC." }}| Field | Tipe | Deskripsi |
|---|---|---|
status | string | Salah satu dari error, fault, rejected, batch_exception |
message | string | null | Alasan kegagalan dari outlet atau pipeline distribusi |
takedown.completed
Section titled “takedown.completed”Dipicu satu kali per outlet saat permintaan takedown berhasil. Bentuknya sama dengan delivery.completed ditambah flag takedown: true.
{ "event": "takedown.completed", "timestamp": "2026-05-18T10:00:00+00:00", "webhook_id": "123", "data": { "distro_queue_id": 456, "release_id": 789, "release_cat": "ABC123", "outlet_id": 12, "outlet_name": "Spotify", "status": "complete", "takedown": true }}release.distributed
Section titled “release.distributed”Dipicu satu kali per rilis saat rilis beralih ke status pengiriman distributed. Hanya dipicu pada saat peralihan menjadi distributed, bukan pada penyimpanan berikutnya selama rilis sudah berstatus distributed.
{ "event": "release.distributed", "timestamp": "2026-05-18T10:00:00+00:00", "webhook_id": "123", "data": { "release_id": 789, "release_cat": "ABC123", "release_title": "Summer EP", "delivery_status": "distributed" }}release.review.status_changed
Section titled “release.review.status_changed”Dipicu setiap kali sebuah rilis berpindah antar status peninjauan.
{ "event": "release.review.status_changed", "timestamp": "2026-05-18T10:00:00+00:00", "webhook_id": "123", "data": { "release_id": 789, "release_cat": "ABC123", "release_title": "Summer EP", "previous_status": "to_review", "new_status": "approved" }}| Field | Tipe | Deskripsi |
|---|---|---|
previous_status | string | Status sebelumnya. Salah satu dari draft, to_review, approved, rejected, require_changes, audit |
new_status | string | Status baru. Kumpulan nilai yang sama |
payment.statement_ready
Section titled “payment.statement_ready”Dipicu saat laporan pembayaran dibuat dan siap untuk dilihat.
{ "event": "payment.statement_ready", "timestamp": "2026-05-18T10:00:00+00:00", "webhook_id": "123", "data": { "payment_request_id": 1024, "invoice_number": "INV-2026-001", "period": "2026-04-30", "amount": 1234.56, "total_due_usd": 1234.56, "currency": "USD" }}| Field | Tipe | Deskripsi |
|---|---|---|
payment_request_id | integer | ID permintaan pembayaran internal |
invoice_number | string | Referensi faktur untuk laporan tersebut |
period | string | null | Tanggal akhir periode (tanggal ISO 8601, YYYY-MM-DD) |
amount | number | Jumlah laporan dalam currency |
total_due_usd | number | Total laporan yang dikonversi ke USD |
currency | string | Kode mata uang ISO 4217 (default-nya USD) |
Memverifikasi Signature Webhook
Section titled “Memverifikasi Signature Webhook”Setiap pengiriman webhook ditandatangani sehingga Anda dapat memverifikasi bahwa pengiriman tersebut memang berasal dari LabelGrid. Selalu verifikasi signature sebelum memproses peristiwa.
Header Permintaan
Section titled “Header Permintaan”Setiap permintaan POST webhook menyertakan header berikut:
| Header | Deskripsi |
|---|---|
X-Webhook-Signature | HMAC-SHA256 dari body permintaan mentah, heksadesimal huruf kecil, tanpa awalan algoritma |
X-Webhook-Timestamp | Timestamp ISO 8601 pengiriman (nilainya sama dengan properti timestamp di body) |
X-Webhook-Event | Pengidentifikasi peristiwa (mis. delivery.completed) |
X-Webhook-Id | ID webhook yang menerima pengiriman |
User-Agent | LabelGrid-Webhooks/1.0 |
Content-Type | application/json |
Algoritma
Section titled “Algoritma”- Algoritma: HMAC-SHA256
- Pengodean: Heksadesimal huruf kecil
- Awalan: Tidak ada; nilainya hanya berupa hex digest, bukan
sha256=... - Konten yang ditandatangani: Seluruh body permintaan JSON mentah (body itu sendiri menyertakan timestamp sebagai properti, sehingga timestamp ikut ditandatangani secara implisit)
Resep Verifikasi
Section titled “Resep Verifikasi”- Baca body permintaan mentah sebelum parsing atau transformasi JSON apa pun. Menserialisasi ulang JSON yang sudah diparsing dapat menghasilkan byte yang berbeda dan merusak signature.
- Hitung
HMAC-SHA256(raw_body, your_webhook_secret)lalu ambil hex digest huruf kecilnya. - Bandingkan dengan
X-Webhook-Signaturemenggunakan perbandingan constant-time. - (Disarankan) Tolak permintaan jika
X-Webhook-Timestamplebih lama dari jendela toleransi replay Anda; kami menyarankan 5 menit.
Contoh PHP
Section titled “Contoh PHP”$rawBody = file_get_contents('php://input');$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';$timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
$expected = hash_hmac('sha256', $rawBody, $webhookSecret);
if (! hash_equals($expected, $signature)) { http_response_code(401); exit('Invalid signature');}
// Reject deliveries older than 5 minutesif (abs(time() - strtotime($timestamp)) > 300) { http_response_code(401); exit('Stale delivery');}
$payload = json_decode($rawBody, true);// ... process the eventhttp_response_code(200);Contoh Node.js
Section titled “Contoh Node.js”const crypto = require('crypto');
// Express: capture raw body BEFORE any JSON middlewareapp.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => { const rawBody = req.body; // Buffer const signature = req.header('X-Webhook-Signature') || ''; const timestamp = req.header('X-Webhook-Timestamp') || '';
const expected = crypto .createHmac('sha256', webhookSecret) .update(rawBody) .digest('hex');
const sigBuf = Buffer.from(signature, 'hex'); const expBuf = Buffer.from(expected, 'hex');
if (sigBuf.length !== expBuf.length || !crypto.timingSafeEqual(sigBuf, expBuf)) { return res.status(401).send('Invalid signature'); }
if (Math.abs(Date.now() - new Date(timestamp).getTime()) > 5 * 60 * 1000) { return res.status(401).send('Stale delivery'); }
const payload = JSON.parse(rawBody.toString('utf8')); // ... process the event res.sendStatus(200);});Contoh Python
Section titled “Contoh Python”import hmac, hashlibfrom datetime import datetime, timezone
raw_body = request.get_data() # Flask: bytes, before any JSON parsingsignature = request.headers.get('X-Webhook-Signature', '')timestamp = request.headers.get('X-Webhook-Timestamp', '')
expected = hmac.new( webhook_secret.encode('utf-8'), raw_body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, signature): return ('Invalid signature', 401)
delivery_time = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))if abs((datetime.now(timezone.utc) - delivery_time).total_seconds()) > 300: return ('Stale delivery', 401)
return ('', 200) # process the event, then ackKesalahan yang Sering Terjadi
Section titled “Kesalahan yang Sering Terjadi”- Menserialisasi ulang body sebelum hashing. Framework yang otomatis memparsing JSON (Express
express.json(), body permintaan default Laravel) kehilangan byte aslinya. Tangkap body mentah terlebih dahulu. - Menggunakan perbandingan yang bukan constant-time (
==,===). Rentan terhadap serangan timing; selalu gunakanhash_equals(PHP),crypto.timingSafeEqual(Node),hmac.compare_digest(Python), atau padanannya di bahasa Anda. - Mengharapkan awalan
sha256=. Nilai header hanyalah hex digest tanpa awalan. - Melewatkan pemeriksaan timestamp. Tanpa itu, signature yang bocor dapat diputar ulang tanpa batas.
Batasan dan Keandalan
Section titled “Batasan dan Keandalan”Batasan Permintaan
Section titled “Batasan Permintaan”| Batasan | Nilai |
|---|---|
| Batas waktu permintaan | 10 detik |
| Ukuran payload maksimum | 64 KB |
| Webhook maksimum per pengguna | 10 |
Jika endpoint Anda tidak merespons dalam 10 detik, pengiriman dianggap gagal dan dicoba ulang.
Jadwal Percobaan Ulang
Section titled “Jadwal Percobaan Ulang”Jika endpoint Anda mengembalikan status non-2xx atau melewati batas waktu, LabelGrid mencoba ulang dengan backoff eksponensial:
| Percobaan | Jeda sebelum percobaan ulang |
|---|---|
| 1 → 2 | 30 detik |
| 2 → 3 | 1 menit |
| 3 → 4 | 2 menit |
| 4 → 5 | 4 menit |
| 5 → 6 | 8 menit |
| 6 → 7 | 16 menit |
| 7 → 8 | 32 menit |
| 8 → 9 | 64 menit |
| 9 → 10 | 128 menit |
Setiap interval mencakup jitter 0–30 detik. Setelah 10 percobaan (total waktu berlalu sekitar 4,5 jam), pengiriman dicatat sebagai gagal permanen dan tidak dicoba ulang lagi.
Penonaktifan Otomatis
Section titled “Penonaktifan Otomatis”Jika sebuah webhook mengakumulasi 100 pengiriman gagal secara kumulatif, webhook tersebut akan dinonaktifkan secara otomatis. Aktifkan kembali secara manual dalam daftar webhook setelah Anda memperbaiki endpoint Anda. Mengaktifkan kembali webhook yang dinonaktifkan akan mengatur ulang hitungan kegagalannya.
Praktik Terbaik untuk Keandalan
Section titled “Praktik Terbaik untuk Keandalan”- Kembalikan respons 2xx dengan cepat (dalam 10 detik)
- Proses data secara asinkron setelah memberi konfirmasi
- Verifikasi signature pada setiap permintaan (lihat Memverifikasi Signature Webhook)
- Pantau hitungan kegagalan Anda dalam daftar webhook
- Periksa log pengiriman saat menyelidiki peristiwa yang terlewat
Kasus Penggunaan
Section titled “Kasus Penggunaan”Notifikasi Otomatis
Section titled “Notifikasi Otomatis”- Kirim pesan Slack saat rilis tayang
- Kirim email ke tim Anda saat pengiriman gagal
- Perbarui dasbor internal
Otomasi Alur Kerja
Section titled “Otomasi Alur Kerja”- Picu kampanye pemasaran saat rilis didistribusikan
- Perbarui situs web Anda saat konten baru tersedia
- Sinkronkan status ke alat manajemen proyek eksternal
Pemantauan dan Peringatan
Section titled “Pemantauan dan Peringatan”- Dapatkan peringatan seketika untuk kegagalan pengiriman
- Pantau perkembangan distribusi secara waktu nyata
- Pantau perubahan status peninjauan
Pemecahan Masalah
Section titled “Pemecahan Masalah”Webhook Tidak Menerima Peristiwa
Section titled “Webhook Tidak Menerima Peristiwa”- Periksa status - Apakah webhook berstatus Active?
- Verifikasi URL - Apakah endpoint dapat diakses dari internet?
- Periksa peristiwa - Apakah peristiwa yang tepat sudah dipilih?
- Tinjau log - Adakah kesalahan yang tercatat?
Hitungan Kegagalan Tinggi
Section titled “Hitungan Kegagalan Tinggi”- Periksa endpoint Anda - Apakah mengembalikan 200 OK?
- Periksa waktu respons - Apakah merespons dalam batas waktu?
- Tinjau pesan kesalahan - Apa yang gagal?
- Uji secara manual - Kirim webhook uji
Membuat Ulang Secret
Section titled “Membuat Ulang Secret”Jika secret webhook Anda bocor:
- Klik Regenerate Secret di pengaturan webhook
- Perbarui aplikasi Anda dengan secret baru
- Secret lama langsung berhenti berfungsi
Butuh Bantuan?
Section titled “Butuh Bantuan?”Jika Anda memiliki pertanyaan tentang webhook, hubungi tim dukungan kami.
Belum menggunakan LabelGrid?
Semua yang baru saja Anda baca tersedia di platform kami.
Lihat kemampuan LabelGrid →