Webhooks
Daftarkan endpoint di Dashboard → Webhooks (atau API). Pilih events yang dibutuhkan. Delivery asynchronous dengan retry backoff.
Payload
{
"event": "shipment.booked",
"created_at": "2026-09-17T04:00:00.000Z",
"data": { "shipment_number": "AP-20260917-XXXX", "awb": "…" }
}At-least-once delivery: event yang sama bisa terkirim lebih dari sekali (retry). Simpan X-AntarPaket-Delivery-Id dan proses satu kali. Event bisa saja datang tidak berurutan — bandingkan timestamp data, bukan urutan kedatangan.
Headers
| Header | Isi |
|---|---|
| X-AntarPaket-Event | nama event, mis. shipment.booked |
| X-AntarPaket-Delivery-Id | ID unik per percobaan delivery |
| X-AntarPaket-Timestamp | unix detik saat pengiriman |
| X-AntarPaket-Signature | v1=<hex> |
Verifikasi signature
Signature = HMAC_SHA256(secret, "<timestamp>.<rawBody>"). Wajib memakai raw body — verifikasi terhadap JSON yang sudah di-parse/reserialize akan gagal. Verifikasi juga timestamp (tolak > 5 menit) untuk anti-replay.
Node.js
import crypto from "crypto";
export function verify(req, secret) {
const raw = req.body; // RAW body (belum di-JSON.parse)
const ts = req.headers["x-antarpaket-timestamp"];
const sig = req.headers["x-antarpaket-signature"]; // "v1=<hex>"
const expected = crypto
.createHmac("sha256", secret)
.update(ts + "." + raw)
.digest("hex");
const ok = crypto.timingSafeEqual(
Buffer.from(sig.slice(3)), Buffer.from(expected)
);
const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300; // 5 menit
return ok && fresh;
}PHP
<?php
$raw = file_get_contents("php://input"); // RAW body
$ts = $_SERVER["HTTP_X_ANTARPAKET_TIMESTAMP"] ?? "";
$sig = $_SERVER["HTTP_X_ANTARPAKET_SIGNATURE"] ?? ""; // "v1=<hex>"
$expected = hash_hmac("sha256", $ts . "." . $raw, $secret);
$ok = hash_equals($expected, substr($sig, 3)); // buang prefix "v1="
$fresh = abs(time() - (int)$ts) < 300;
if (!($ok && $fresh)) { http_response_code(400); exit; }
// aman: proses json_decode($raw, true)Events
| Event | Trigger |
|---|---|
shipment.created | Draft pengiriman dibuat |
shipment.payment_pending | Menunggu pembayaran |
shipment.payment_paid | Pembayaran diterima |
shipment.booked | Resi/AWB terbit |
shipment.booking_failed | Booking gagal (dana aman) |
shipment.picked_up | Paket dijemput |
shipment.in_transit | Dalam perjalanan |
shipment.out_for_delivery | Sedang diantar |
shipment.delivered | Terkirim |
shipment.delivery_failed | Gagal dikirim |
shipment.returned | Dikembalikan |
shipment.cancelled | Dibatalkan |
wallet.topup.paid | Top up berhasil |
webhook.test | Test koneksi (test-only) |
Retry & keandalan
- Response 2xx = sukses. Selain itu (atau timeout 10 detik) = gagal → retry backoff (30s → 1m → 2m → … maks 1 jam, total 6 attempt).
- Setelah attempt terakhir gagal → delivery EXHAUSTED; endpoint bisa auto-disable setelah 20 kegagalan beruntun (dashboard memperingatkan).
- Business state AntarPaket tidak pernah berubah karena webhook gagal — retry aman.
- Endpoint yang tidak dipakai sebaiknya dimatikan lewat dashboard.
Test & keamanan
- Tombol Kirim Test mengirim event
webhook.testsungguhan (signed) — cek di log server kamu. - URL harus HTTPS di production. localhost/private IP/metadata endpoint diblokir (SSRF protection).
- Rotasi secret dari dashboard — secret lama tidak berlaku untuk delivery berikutnya.
- Jangan taruh API key AntarPaket di client/browser — integrasi dari server-side.