Skip to navigation

Webhooks & Real-Time Event Telemetry

Lipafy sends HTTPS POST webhooks in real time whenever payment intents transition lifecycle states or telecom receipts are reconciled.


🔔 Event Types

Event NameTrigger Condition
payment_intent.createdIntent drafted by an operator or agent.
payment_intent.approval_requiredIntent exceeds policy threshold and requires human sign-off.
payment_intent.approvedSignatory signed the intent snapshot; queued for network execution.
payment_intent.succeededTelecom network confirmed final settlement; receipt number issued.
payment_intent.failedTransaction declined by network or rejected by policy engine.
stk_collection.succeededCustomer entered M-Pesa PIN on their phone for chat checkout.

🔒 Verifying HMAC-SHA256 Signatures

Every webhook includes a X-Lipafy-Signature header computed using your workspace Webhook Secret:

import crypto from 'crypto'
export function verifyLipafyWebhook(
payloadRawBody: string,
signatureHeader: string,
webhookSecret: string
): boolean {
const [timestampStr, signature] = signatureHeader.split(',').map((s) => s.split('=')[1])
// Guard against replay attacks (reject payloads older than 5 minutes)
const currentTime = Math.floor(Date.now() / 1000)
if (Math.abs(currentTime - parseInt(timestampStr, 10)) > 300) {
return false
}
const signedPayload = `${timestampStr}.${payloadRawBody}`
const expectedSignature = crypto
.createHmac('sha256', webhookSecret)
.update(signedPayload)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expectedSignature, 'hex')
)
}

📦 Webhook Payload Example (payment_intent.succeeded)

{
"id": "evt_0194820a-8401-7b92",
"event": "payment_intent.succeeded",
"created_at": "2026-08-17T00:30:00Z",
"data": {
"payment_intent": {
"id": "pi_9f83a2bc-d14e-4b92-8051-18239014819a",
"status": "SUCCEEDED",
"amount_minor": "300000",
"fee_minor": "0",
"currency": "KES",
"network_receipt": "QHK892N1P",
"destination": "PayBill 150501 (Ref: 0722123456)",
"purpose": "Monthly Home Fibre Renewal",
"metadata": {
"agent_id": "claude-code-home-pilot"
}
}
}
}

Delivery ordering across distributed consumers is resilient against out-of-order deliveries. Applications should deduplicate events by event ID and inspect the payment intent state. Only SUCCEEDED, FAILED, or CANCELLED represent terminal finality; PROCESSING indicates an in-flight transaction.