Webhook Events (Notiblast → Endpoint Anda)
Halaman ini bukan endpoint Notiblast. Ini kontrak POST yang Notiblast kirim ke URL webhook milik Anda — Anda yang mengimplementasikan endpoint penerimanya.URL dipasang per channel (whatsapp, push) oleh admin company di dasbor Notiblast → pengaturan Webhook. Secret penanda tangan hanya ditampilkan sekali, saat URL pertama kali disimpan atau saat secret diputar ulang — simpan saat itu juga.| Event | Channel | Kapan dikirim |
|---|
message.received | whatsapp | Setiap pesan masuk, dan setiap kali pesan itu di-Replay dari dasbor |
push.completed | push | Sekali, saat satu Pesan Push selesai diproses (sent / partial / failed) |
1. Request yang kami kirim#
| Header | Isi |
|---|
X-Notiblast-Event-Id | Sama dengan id di body. Stabil lintas percobaan dan lintas Replay — pakai sebagai kunci idempotency |
X-Notiblast-Timestamp | Unix detik saat POST dibuat |
X-Notiblast-Delivery-Attempt | Percobaan ke-berapa, 1–4 |
X-Notiblast-Signature | t=<unix detik>,v1=<hex> — lihat §4 |
Semua event memakai envelope yang sama:{
"id": "string (24 hex)",
"type": "message.received | push.completed",
"channel": "whatsapp | email | push",
"created_at": "ISO 8601",
"data": { }
}
2. message.received#
Body#
| Field | Tipe | Keterangan |
|---|
id | string (24 hex) | _id pesan masuk — kunci idempotency |
type | "message.received" | |
channel | "whatsapp" | "email" | |
created_at | string (date-time) | Saat pesan dicatat Notiblast |
data.external_id | string | Id pesan dari provider (WhatsApp message id / Message-ID email) |
data.wa_number | string (24 hex) | null | _id nomor WhatsApp penerima; null untuk email |
data.from | string | Pengirim |
data.to | string | Penerima (nomor / alamat Anda) |
data.message_type | string | text, image, audio, video, document, sticker, location, contact, unsupported |
data.text | string | null | Isi teks |
data.media | object | null | { mime_type, size, filename, caption, provider_media_id } — size, filename, caption, provider_media_id boleh null |
data.received_at | string (date-time) | Saat provider menerima pesan |
Isi mentah provider (payload) dan catatan pengiriman webhook (delivery) sengaja tidak ikut.Contoh#
{
"id": "507f1f77bcf86cd799439099",
"type": "message.received",
"channel": "whatsapp",
"created_at": "2026-08-26T04:15:12.000Z",
"data": {
"external_id": "3EB0C767D26B8A1B2C3D",
"wa_number": "507f1f77bcf86cd799439081",
"from": "6281234567890",
"to": "6289876543210",
"message_type": "text",
"text": "Halo, pesanan saya sudah dikirim?",
"media": null,
"received_at": "2026-08-26T04:15:11.000Z"
}
}
3. push.completed#
Body#
| Field | Tipe | Keterangan |
|---|
id | string (24 hex) | _id Pesan Push — kunci idempotency |
type | "push.completed" | |
channel | "push" | |
created_at | string (date-time) | Saat Pesan Push dibuat |
data.push_app | string (24 hex) | _id Push App |
data.push_app_name | string | |
data.title | string | Judul notifikasi |
data.target_type | string | external_id, list, all, token, topic |
data.target_label | string | Label sasaran yang mudah dibaca |
data.status | string | Hasil akhir: sent, partial, failed |
data.stats | object | { devices_targeted, devices_sent, devices_failed, devices_skipped, external_ids_unmatched } — semuanya 0 bila target_type = "topic". devices_skipped = perangkat permission_denied / unsubscribed, bukan kegagalan |
data.error | string | null | Alasan bila failed |
data.finished_at | string (date-time) | null | Saat pemrosesan selesai |
Isi notifikasi (body) sengaja tidak ikut — bisa memuat data pribadi penerima.Contoh#
{
"id": "507f1f77bcf86cd799439077",
"type": "push.completed",
"channel": "push",
"created_at": "2026-09-16T04:01:31.900Z",
"data": {
"push_app": "507f1f77bcf86cd799439011",
"push_app_name": "Toko Maju — Pelanggan",
"title": "Promo akhir pekan",
"target_type": "all",
"target_label": "Semua perangkat",
"status": "partial",
"stats": {
"devices_targeted": 4102,
"devices_sent": 4037,
"devices_failed": 65,
"devices_skipped": 214,
"external_ids_unmatched": 0
},
"error": null,
"finished_at": "2026-09-16T04:01:31.900Z"
}
}
4. Verifikasi tanda tangan#
v1 = HMAC-SHA256(secret, "<t>.<rawBody>") dalam hex.1.
Pisahkan t dan v1 dari header X-Notiblast-Signature.
2.
Tolak bila |now − t| > 300 detik (mencegah replay lalu-lintas lama).
3.
Hitung HMAC atas raw body persis yang diterima — jangan JSON.stringify ulang hasil parse; beda satu spasi saja membuat tanda tangan selalu gagal.
4.
Bandingkan dengan timingSafeEqual, bukan ===.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyNotiblast(rawBody: string, header: string, secret: string): boolean {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=") as [string, string]));
const t = Number(parts.t);
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`, "utf8").digest();
const received = Buffer.from(parts.v1 ?? "", "hex");
return received.length === expected.length && timingSafeEqual(received, expected);
}
Prefix v1= diberi versi: bila algoritma diganti nanti, header yang sama akan menambah v2=, bukan mengganti v1=.
5. Retry & jawaban yang kami harapkan#
| Jawaban endpoint Anda | Yang Notiblast lakukan |
|---|
2xx | Selesai — delivery.status: delivered |
408, 429, 5xx, timeout (10 detik), DNS / TCP error | Dicoba ulang. Total 4 percobaan: langsung, +30 detik, +15 menit, +30 menit. Setelah itu failed |
4xx lain (400, 401, 403, 404, 410, …) | Dianggap penolakan permanen — failed seketika, tidak dicoba ulang |
| Belum ada webhook aktif untuk channel itu | Tidak dikirim — delivery.status: skipped |
Body balasan Anda diabaikan; hanya status code yang dibaca.6. Yang harus Anda lakukan#
Jawab 2xx secepatnya, proses secara asinkron. Batas kami 10 detik per percobaan.
Deduplikasi dengan id. Event yang sama bisa datang lebih dari sekali (retry, Replay).
Verifikasi tanda tangan sebelum mempercayai isi body.
Endpoint Anda yang mati tidak mempengaruhi pengiriman pesan itu sendiri — hanya pengabarannya yang gagal. Status pengirimannya terlihat di dasbor (detail pesan masuk / Pesan Push).
Modified at 2026-10-21 07:54:52