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

HeaderIsi
X-AntarPaket-Eventnama event, mis. shipment.booked
X-AntarPaket-Delivery-IdID unik per percobaan delivery
X-AntarPaket-Timestampunix detik saat pengiriman
X-AntarPaket-Signaturev1=<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

EventTrigger
shipment.createdDraft pengiriman dibuat
shipment.payment_pendingMenunggu pembayaran
shipment.payment_paidPembayaran diterima
shipment.bookedResi/AWB terbit
shipment.booking_failedBooking gagal (dana aman)
shipment.picked_upPaket dijemput
shipment.in_transitDalam perjalanan
shipment.out_for_deliverySedang diantar
shipment.deliveredTerkirim
shipment.delivery_failedGagal dikirim
shipment.returnedDikembalikan
shipment.cancelledDibatalkan
wallet.topup.paidTop up berhasil
webhook.testTest 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.test sungguhan (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.
Webhooks — AntarPaket API