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:

1import crypto from 'crypto'
2
3export function verifyLipafyWebhook(
4 payloadRawBody: string,
5 signatureHeader: string,
6 webhookSecret: string
7): boolean {
8 const [timestampStr, signature] = signatureHeader.split(',').map((s) => s.split('=')[1])
9
10 // Guard against replay attacks (reject payloads older than 5 minutes)
11 const currentTime = Math.floor(Date.now() / 1000)
12 if (Math.abs(currentTime - parseInt(timestampStr, 10)) > 300) {
13 return false
14 }
15
16 const signedPayload = `${timestampStr}.${payloadRawBody}`
17 const expectedSignature = crypto
18 .createHmac('sha256', webhookSecret)
19 .update(signedPayload)
20 .digest('hex')
21
22 return crypto.timingSafeEqual(
23 Buffer.from(signature, 'hex'),
24 Buffer.from(expectedSignature, 'hex')
25 )
26}

📦 Webhook Payload Example (payment_intent.succeeded)

1{
2 "id": "evt_0194820a-8401-7b92",
3 "event": "payment_intent.succeeded",
4 "created_at": "2026-08-17T00:30:00Z",
5 "data": {
6 "payment_intent": {
7 "id": "pi_9f83a2bc-d14e-4b92-8051-18239014819a",
8 "status": "SUCCEEDED",
9 "amount_minor": "300000",
10 "fee_minor": "0",
11 "currency": "KES",
12 "network_receipt": "QHK892N1P",
13 "destination": "PayBill 150501 (Ref: 0722123456)",
14 "purpose": "Monthly Home Fibre Renewal",
15 "metadata": {
16 "agent_id": "claude-code-home-pilot"
17 }
18 }
19 }
20}

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.