Webhooks
Webhooks are how you safely confirm a payment. Register HTTPS endpoints in the console; Mavunta signs every event and retries failed deliveries with backoff. Return any 2xx to acknowledge.
Event types
payment_intent.paid payment_intent.failed
payment_intent.expired payment_intent.cancelled
payment_intent.underpaid payment_intent.overpaid
payment_intent.refunded payment_intent.requires_review
payment_intent.disputed payment_intent.duplicate_paymentVerify the signature
Each request carries Mavunta-Signature (a hex HMAC), Mavunta-Timestamp, and Mavunta-Event-Id. Recompute the HMAC-SHA256 over `${timestamp}.${rawBody}` with your endpoint secret and compare in constant time. Mavunta-Event-Id is stable across retries — use it to deduplicate.
import { createHmac, timingSafeEqual } from 'node:crypto'
function verify(secret, timestamp, rawBody, signature) {
const expected = createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`).digest('hex')
return timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(signature, 'hex'))
}mavunta.webhooks.verify(...) checks the signature and timestamp and returns the parsed event. See SDKs.Payload
{
"id": "evt_live_...",
"type": "payment_intent.paid",
"api_version": "2026-06-01",
"created_at": "2026-06-18T12:06:10Z",
"data": {
"id": "pi_...",
"status": "paid",
"amount": "2500",
"currency": "KES",
"payment_method": "mpesa",
"settlement_currency": "USDT",
"merchant_reference": "ORDER-1001"
}
}Test locally with the CLI
Forward live sandbox events to your local server, no public URL or deploy needed — the fastest way to build a webhook handler:
npm install -g @mavunta/cli
export MAVUNTA_SECRET_KEY=cwk_test_sk_...
mavunta listen --forward-to http://localhost:3000/webhooks/mavunta
# then, in another terminal:
mavunta trigger payment_intent.paidlisten prints a whsec_cli_…secret to verify the forwarded events with. Use the console's Sandbox tester to drive richer scenarios.
Retries and testing
A failed delivery is retried with exponential backoff for several attempts. Each endpoint has its own signing secret, which you can rotate or pause at any time (deploy the new secret first). Send a test event and inspect deliveries from the console's Sandbox tester and webhook manager.
You can also inspect and replay events over the API:
GET /v1/webhook_events # list events (webhooks:read)
GET /v1/webhook_events/:id
POST /v1/webhook_events/:id/resend # re-enqueue delivery (webhooks:write)