Event yang
sampe — selalu
HMAC-SHA256 signed, multi-secret rotation, 8 retry attempts selama 31 jam dengan jitter. SSRF defense built-in. Plus delivery log searchable + manual replay.
Signature yang reviewable
Format yang sudah jadi industry standard. Timestamp masuk signed payload — replay attack mati di pintu.
Multi-secret rotation tanpa downtime
Punya dua atau lebih secret yang aktif bareng. Rotate kapan pun tanpa risiko outage — verifier cocokin signature dengan salah satu secret yang valid.
- HMAC-SHA256 over
${timestamp}.${rawBody} - Timestamp di-include dalam signed payload — replay-safe
- Constant-time compare via SDK helper — tidak ada timing attack
- Reject timestamp skew lebih dari 5 menit
# Header format (Stripe-style)
X-Kirim-Signature: t=1717012345,v1=<hex>,v1=<hex>
X-Kirim-Event: message.received
X-Kirim-Event-Id: evt_01JFK...
X-Kirim-Delivery-Id: dlv_01JFK...
X-Kirim-Attempt: 1
X-Kirim-Source: kirimdev
# Verification:
# 1. Parse t (unix timestamp) + all v1 signatures
# 2. Compute HMAC-SHA256 of `${t}.${rawBody}` with each secret
# 3. Any match → valid (constant-time compare)
# 4. Reject if t skew > 5 minutes SDK helper untuk verify
Tidak perlu re-implement HMAC compare sendiri. verifyWebhookSignature dari @kirimdev/sdk/webhooks handle parsing, constant-time compare, dan timestamp validation.
Bekerja sama di Hono, Express, Next.js Route Handlers, atau plain Web fetch handler.
import { Hono } from 'hono'
import { verifyWebhookSignature }
from '@kirimdev/sdk/webhooks'
const app = new Hono()
app.post('/webhook', async (c) => {
await verifyWebhookSignature({
rawBody: await c.req.text(),
signatureHeader: c.req.header('x-kirim-signature'),
secrets: [
process.env.WEBHOOK_SECRET_CURRENT!,
process.env.WEBHOOK_SECRET_PREVIOUS! // rotation
]
})
const event = c.req.header('x-kirim-event')
const payload = await c.req.json()
if (event === 'message.received') {
await handleIncoming(payload)
}
return c.json({ ok: true })
}) 12 event types
Subscribe yang Anda butuhkan — masing-masing subscription bisa pilih event set sendiri.
Delivery yang tidak menyerah
Network hiccup? Endpoint Anda lagi maintenance? Tidak masalah. 8 attempts selama ~31 jam dengan exponential backoff + jitter.
Exponential backoff
10s → 30s → 2m → 10m → 1h → 6h → 24h, dengan ±20% jitter. Retryable: 408, 429, 5xx, network error.
Delivery log + replay
Setiap delivery attempt di-log. Cari, filter, dan replay manual via GET /v1/webhook_deliveries atau dashboard.
SSRF defense 2-layer
API boundary reject loopback, private IP, DNS rebinding tricks. Worker re-resolve di delivery time.
No redirect follow
3xx tidak di-follow — mencegah signature leak ke endpoint yang tidak terduga.
Multi-secret support
Generate secret baru, deploy, lalu deactivate lama. Tidak ada window outage.
Auto-disable
Subscription yang fail terus-menerus akan di-mark inactive — tidak burn quota di-resource yang sudah dead.
Subscribe via API atau dashboard
Buat subscription via SDK, REST API, atau dashboard UI. Per-org subscriptions di-scope ke events yang Anda pilih. Update events tanpa drop subscription.
const sub = await kirim.webhookSubscriptions.create({
url: 'https://api.acme.com/kirim-webhook',
events: [
'message.received',
'message.status',
'conversation.assigned'
]
})
// Multi-secret untuk rotation
await kirim.webhookSubscriptions.addSecret(sub.id) Webhook yang tidak perlu di-monitor
Set once, forget. Delivery log selalu ada kalau perlu debug.