Webhooks
Kashia sends webhook notifications to your server when events occur. Configure your webhook URL in Dashboard → Integrations. All webhooks are signed with your webhook secret for verification.
Failed deliveries are retried up to 3 times with exponential backoff.
Webhook payload format
json
{
"event": "escrow.completed",
"data": {
"id": "escrow-uuid",
"reference": "ESC-a1b2c3d4",
"status": "completed",
"amount": 100000000,
"currency": "NGN",
"platform_fee": 1500000,
"merchant_fee": 10000000,
"total_fees": 11500000,
"total_amount": 111500000,
"seller_amount": 100000000,
"buyer": { "id": "...", "email": "..." },
"seller": { "id": "...", "email": "..." },
"metadata": { "order_id": "ORD-12345" }
},
"timestamp": "2025-01-15T10:30:00Z",
"webhook_id": "webhook-event-uuid"
}Escrow events include fee breakdown fields (amounts in kobo):
| Field | Description |
|---|---|
amount | Base transaction amount — what the seller receives on release |
platform_fee | Kashia platform fee charged to the buyer |
merchant_fee | Marketplace commission credited to the merchant (0 for single-vendor) |
total_fees | Sum of platform_fee + merchant_fee |
total_amount | Total paid by the buyer (amount + total_fees) |
seller_amount | Amount released to the seller — equals amount |
Signature verification
Kashia signs every webhook with your webhook secret using HMAC-SHA256. The signature is sent in the X-Kashia-Signature header.
Header
X-Kashia-Signature: sha256=5d5b9e7c8b...Verification examples
JavaScript
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expectedSignature = 'sha256=' +
crypto.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
"code-hl-comment">// In your webhook handler
app.post('/webhooks/kashia', (req, res) => {
const signature = req.headers['x-kashia-signature'];
if (!verifyWebhook(req.body, signature, process.env.KASHIA_WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const { event, data } = req.body;
switch (event) {
case 'escrow.active':
"code-hl-comment">// Payment received — update your order status
break;
case 'escrow.completed':
"code-hl-comment">// Delivery confirmed — funds released
break;
case 'escrow.disputed':
"code-hl-comment">// Dispute opened — notify relevant parties
break;
}
res.status(200).send('OK');
});Event types
| Event | Triggered When |
|---|---|
payment_link.created | A new payment link is created |
payment_link.expired | A payment link expires |
escrow.created | An escrow is created (payment pending) |
escrow.active | Payment received, funds locked |
escrow.awaiting_confirmation | Seller marked as delivered — includes shipping_details in payload when provided |
escrow.completed | Buyer confirmed, funds released |
escrow.refunded | Full refund issued |
escrow.cancelled | Escrow cancelled |
escrow.disputed | Dispute opened |
dispute.opened | A new dispute is opened |
dispute.escalated | Dispute escalated to Kashia admin |
dispute.resolved | Dispute resolved |
withdrawal.initiated | Withdrawal accepted and processing with Monnify |
withdrawal.successful | Funds sent to the user's bank account |
withdrawal.failed | Withdrawal failed — funds returned to available balance |
bank_account.added | User added a verified bank account |
Shipping on awaiting confirmation
When a seller marks an escrow as delivered, the escrow.awaiting_confirmation webhook includes a delivery object with tracking, carrier, dispatch contact, notes, and image metadata when provided.
json
{
"event": "escrow.awaiting_confirmation",
"data": {
"id": "escrow-uuid",
"reference": "ESC-a1b2c3d4",
"status": "awaiting_confirmation",
"delivery": {
"shipped_at": "2025-01-15T10:30:00Z",
"tracking_number": "TRK-123456",
"carrier": "DHL",
"carrier_website": "https://dhl.com/track/TRK-123456",
"dispatch_name": "John Doe",
"dispatch_phone": "+2348000000000",
"estimated_delivery_date": "2025-01-20",
"notes": "Left with reception"
}
}
}Withdrawal webhook example
json
{
"event": "withdrawal.successful",
"data": {
"withdrawal_id": "uuid",
"reference": "WTH-a1b2c3d4",
"amount": 9500000,
"net_amount": 9495000,
"status": "successful",
"user_id": "user-uuid"
},
"timestamp": "2025-01-15T10:35:00Z",
"webhook_id": "webhook-event-uuid"
}Retry policy
- First retry: 1 minute after failure
- Second retry: 5 minutes after first retry
- Third retry: 30 minutes after second retry
- After 3 failures: webhook marked as failed
Best practices
- Always return 200 OK quickly — process webhook data asynchronously
- Use the
webhook_idto deduplicate (you may receive the same event more than once) - Verify signatures before processing
- Store webhook payloads for debugging