Skip to main content

Webhooks

Webhooks allow you to receive real-time notifications when events occur in your account. Instead of polling the API, you receive HTTP POST requests at your specified endpoint.

Register a Webhook

curl -X POST https://api.lanonasis.com/v1/webhooks \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/webhooks/lanonasis",
"events": ["transfer.completed", "transfer.failed", "payment.successful"],
"description": "Production webhook"
}'

Response

{
"success": true,
"data": {
"id": "whk_abc123",
"url": "https://yourapp.com/webhooks/lanonasis",
"events": ["transfer.completed", "transfer.failed", "payment.successful"],
"secret": "whsec_EXAMPLE_SECRET_DO_NOT_USE",
"status": "active",
"created_at": "2024-01-15T10:00:00Z"
}
}
Save Your Secret

The secret is only shown once. Save it securely to verify webhook signatures.

Event Types

Transfer Events

EventDescription
transfer.initiatedTransfer created
transfer.processingTransfer is being processed
transfer.completedTransfer successful
transfer.failedTransfer failed
transfer.reversedTransfer was reversed

Payment Events

EventDescription
payment.initializedPayment created
payment.successfulPayment completed
payment.failedPayment failed
payment.refundedPayment refunded

Wallet Events

EventDescription
wallet.createdNew wallet created
wallet.creditedFunds added to wallet
wallet.debitedFunds removed from wallet
wallet.frozenWallet frozen

KYC Events

EventDescription
kyc.submittedVerification submitted
kyc.verifiedVerification successful
kyc.failedVerification failed

Webhook Payload

{
"id": "evt_123abc",
"type": "transfer.completed",
"created_at": "2024-01-15T14:30:05Z",
"data": {
"id": "txn_abc123xyz",
"reference": "TRF-20240115-001",
"amount": 100000,
"fee": 1000,
"currency": "NGN",
"status": "success",
"source_wallet_id": "wal_sender123",
"destination": {
"type": "bank",
"account_number": "0123456789",
"bank_code": "058",
"account_name": "John Doe"
},
"completed_at": "2024-01-15T14:30:05Z"
}
}

Verify Webhook Signature

Always verify webhook signatures to ensure requests are from LanOnasis.

Signature Header

Webhooks include a X-LanOnasis-Signature header:

X-LanOnasis-Signature: t=1704299405,v1=EXAMPLE_HASH_VALUE_DO_NOT_USE

Verification Code

import crypto from "crypto";

function verifyWebhookSignature(
payload: string,
signature: string,
secret: string
): boolean {
const parts = signature.split(",");
const timestamp = parts.find((p) => p.startsWith("t="))?.slice(2);
const hash = parts.find((p) => p.startsWith("v1="))?.slice(3);

if (!timestamp || !hash) return false;

// Check timestamp is recent (within 5 minutes)
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - parseInt(timestamp)) > 300) {
return false;
}

// Compute expected signature
const signedPayload = `${timestamp}.${payload}`;
const expectedHash = crypto
.createHmac("sha256", secret)
.update(signedPayload)
.digest("hex");

return crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(expectedHash));
}

// Express.js example
app.post(
"/webhooks/lanonasis",
express.raw({ type: "application/json" }),
(req, res) => {
const signature = req.headers["x-lanonasis-signature"];
const payload = req.body.toString();

if (
!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)
) {
return res.status(401).send("Invalid signature");
}

const event = JSON.parse(payload);

switch (event.type) {
case "transfer.completed":
handleTransferCompleted(event.data);
break;
case "payment.successful":
handlePaymentSuccessful(event.data);
break;
// ... handle other events
}

res.status(200).send("OK");
}
);

Python Verification

import hmac
import hashlib
import time

def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
parts = dict(p.split('=') for p in signature.split(','))
timestamp = parts.get('t')
hash_value = parts.get('v1')

if not timestamp or not hash_value:
return False

# Check timestamp is recent
if abs(time.time() - int(timestamp)) > 300:
return False

# Compute expected signature
signed_payload = f"{timestamp}.{payload.decode()}"
expected_hash = hmac.new(
secret.encode(),
signed_payload.encode(),
hashlib.sha256
).hexdigest()

return hmac.compare_digest(hash_value, expected_hash)

Retry Policy

If your endpoint doesn't respond with 2xx, we retry with exponential backoff:

AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours
68 hours
724 hours

After 7 failed attempts, the webhook is marked as failed and the endpoint may be disabled.

List Webhooks

curl https://api.lanonasis.com/v1/webhooks \
-H "X-API-Key: YOUR_API_KEY"

Update Webhook

curl -X PATCH https://api.lanonasis.com/v1/webhooks/whk_abc123 \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": ["transfer.completed", "payment.successful"],
"status": "active"
}'

Delete Webhook

curl -X DELETE https://api.lanonasis.com/v1/webhooks/whk_abc123 \
-H "X-API-Key: YOUR_API_KEY"

Best Practices

1. Respond Quickly

Return 200 OK immediately, then process async:

app.post("/webhooks/lanonasis", async (req, res) => {
// Verify signature first

// Acknowledge immediately
res.status(200).send("OK");

// Process async
queueWebhookProcessing(req.body);
});

2. Handle Duplicates

Events may be delivered more than once. Use event.id for deduplication:

const processedEvents = new Set();

function handleWebhook(event) {
if (processedEvents.has(event.id)) {
return; // Already processed
}

processedEvents.add(event.id);
// Process event...
}

3. Secure Your Endpoint

  • Use HTTPS only
  • Verify signatures
  • Use a random URL path

4. Log Everything

Keep logs of all webhook events for debugging:

console.log({
event_id: event.id,
event_type: event.type,
received_at: new Date().toISOString(),
data: event.data,
});

Testing Webhooks

Sandbox Events

In sandbox mode, trigger test events via the dashboard or API.

Local Development

Use a tunneling service like ngrok:

ngrok http 3000

# Register webhook with ngrok URL
curl -X POST https://sandbox.lanonasis.com/v1/webhooks \
-H "X-API-Key: YOUR_TEST_API_KEY" \
-d '{"url": "https://abc123.ngrok.io/webhooks/lanonasis"}'