Webhooks
Event status order dan verifikasi tanda tangan HMAC.
Tujuan webhook
Setiap perubahan status order agent dikirim sebagai POST JSON ke callback_url order itu. Kalau callback_url tidak dikirim saat order dibuat, dipakai webhook URL yang diisi di API key. Tanpa keduanya, tidak ada webhook; pakai polling GET /v1/orders/{id}.
Dashboard hanya menerima webhook URL https:// (atau http://localhost untuk pengembangan). Lewat API, callback_url harus diawali http:// atau https://.
Event
Nama event selalu order.<status baru>:
| Event | Kapan |
|---|---|
| order.dibayar | Dibayar dari deposit (langsung saat order dibuat, atau setelah admin menyetujui) |
| order.ditawarkan | Mulai ditawarkan ke praktisi (juga saat praktisi melepas order) |
| order.diambil | Praktisi menerima order |
| order.dikerjakan | Mulai dikerjakan, atau kembali dikerjakan setelah kamu membalas / revisi |
| order.butuh_input_klien | Praktisi butuh data dari kamu |
| order.diserahkan | Hasil diserahkan, cek deliverable |
| order.selesai | Hasil diterima, escrow dilepas |
| order.sengketa | Order masuk sengketa |
| order.dibatalkan | Order dibatalkan (mis. ditolak admin organisasi) |
| order.direfund | Dana dikembalikan ke deposit |
Belum tersedia
Payload
json
{
"event": "order.diserahkan",
"sent_at": "2026-10-02T09:30:00.000Z",
"order": {
"id": "ord_xxxxxxxxxxxxxxxxxxxx",
"status": "diserahkan",
"service": "pt-perorangan",
"price_idr": 2900000,
"sla_due_at": "2026-10-04T08:00:00.000Z",
"deliverable": "Akta dan SK Kemenkumham terlampir: ...",
"updated_at": "2026-10-02T09:30:00.000Z"
}
}| Header | Nilai |
|---|---|
| Content-Type | application/json |
| User-Agent | Dampingin-Webhook/1 |
| X-Dampingin-Signature | t=<unix detik>,v1=<hex HMAC-SHA256> |
Payload adalah ringkasan. messages tidak ikut; ambil order lengkap untuk membaca pesan praktisi saat order.butuh_input_klien.
Pengiriman dan retry
- Balas dengan status 2xx dalam 10 detik. Status lain, timeout, atau koneksi gagal dianggap gagal.
- Pengiriman yang gagal dicoba lagi dengan backoff eksponensial (sekitar 10 detik, 20 detik, 40 detik, 80 detik), maksimal 5 percobaan per event. Setelah itu event berhenti dikirim.
- Event dikirim oleh worker antrean, jadi bisa datang lebih dari sekali dan tidak dijamin berurutan. Jadikan
order.statusdanorder.updated_atacuan, dan ambil ulang order kalau ragu.
Verifikasi tanda tangan
Setiap request ditandatangani dengan webhook secret milik API key yang membuat order:
text
X-Dampingin-Signature: t=1790500000,v1=5f2c...e9
v1 = hex( HMAC-SHA256( webhook_secret, t + "." + raw_body ) )import { createHmac, timingSafeEqual } from "node:crypto";
/** header = X-Dampingin-Signature, rawBody = request body as received (before JSON.parse). */
export function verifyDampingin(rawBody: string, header: string | null, secret: string, toleranceSec = 300) {
if (!header) return false;
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=") as [string, string]));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(parts.v1 ?? "");
return a.length === b.length && timingSafeEqual(a, b);
}
// Next.js route handler
export async function POST(req: Request) {
const raw = await req.text();
if (!verifyDampingin(raw, req.headers.get("x-dampingin-signature"), process.env.DAMPINGIN_WEBHOOK_SECRET!)) {
return new Response("bad signature", { status: 401 });
}
const { event, order } = JSON.parse(raw);
// Idempotent handling: the same event can arrive more than once.
console.log(event, order.id, order.status);
return new Response(null, { status: 204 });
}Belum tersedia
Praktik yang disarankan
- Balas 2xx secepatnya, proses di belakang (antrean kamu sendiri).
- Simpan pasangan
order.id+event+updated_atyang sudah diproses supaya duplikat diabaikan. - Tetap siapkan polling cadangan untuk order yang lama tidak berubah, karena event yang gagal 5 kali tidak dikirim ulang.
- Lihat juga Order untuk arti setiap status.