Skip to content

Webhooks Overview

AsiliChain emits webhook events to your registered endpoint whenever key protocol events occur. Webhooks allow buyer portals, MFI dashboards, cooperative systems, and third-party integrations to react to on-chain events in real time.

Terminal window
curl -X POST https://api.asilichain.xyz/webhooks \
-H "Authorization: Bearer {cooperative_token}" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-system.example.com/asilichain-webhook",
"events": ["batch.delivered", "batch.exported", "batch.settled", "dds.generated"],
"cooperative_id": "COOP-MBALE-001",
"secret": "your_webhook_signing_secret"
}'

Response:

{
"webhook_id": "wh_01HXYZ...",
"url": "https://your-system.example.com/asilichain-webhook",
"events": ["batch.delivered", "batch.exported", "batch.settled", "dds.generated"],
"status": "ACTIVE",
"created_at": "2026-04-24T10:00:00Z"
}

Every webhook delivery includes an X-AsiliChain-Signature header. Always verify it:

import { createHmac } from 'crypto';
function verifyWebhook(body: string, signature: string, secret: string): boolean {
const expected = createHmac('sha256', secret)
.update(body)
.digest('hex');
return `sha256=${expected}` === signature;
}
// In your webhook handler:
app.post('/asilichain-webhook', (req, res) => {
const isValid = verifyWebhook(
JSON.stringify(req.body),
req.headers['x-asilichain-signature'],
process.env.WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process event
const { event, data } = req.body;
console.log('Received:', event, data);
res.json({ received: true });
});
EventTriggerUseful for
farmer.registeredFarmer added to FarmerRegistryCooperative CRM sync
batch.deliveredBatchToken mintedMFI dashboards showing new collateral
batch.gradedQuality assessment recordedDDS eligibility tracking
batch.warehousedStorage confirmedBuyer portal inventory update
batch.committedPurchaseOrder confirmedExporter workflow
batch.exportedUCDA export confirmedLoan repayment trigger (internal) / buyer portal
batch.settledAuto-repayment complete, net disbursedMFI yield reporting
loan.originatedLoan disbursed to farmerMFI loan book update
loan.repaidLoan fully settledMFI yield confirmation
loan.defaultedForbearance expired without repaymentMFI risk alert
dds.generatedDDS PDF created and IPFS-pinnedExporter compliance workflow
dds.filedTRACES reference confirmedCustoms documentation complete
credit.updatedCreditScore changedMFI credit decision systems
payout.completedFarmer MTN MoMo creditedCooperative farmer management
payout.failedPayout retries exhaustedCooperative operations alert
{
"webhook_id": "wh_01HXYZ...",
"event": "batch.exported",
"api_version": "2026-04-01",
"created_at": "2026-04-20T11:55:00Z",
"data": {
"batch_id": "BATCH-2026-004821",
"token_id": 4821,
"farmer_id": "UG-KAS-2024-001234",
"cooperative_id": "COOP-MBALE-001",
"weight_kg": 67.5,
"grade": "screen18",
"ucda_export_permit": "UCDA-EXP-2026-04821",
"mantle_tx_hash": "0xcde456...",
"hedera_sequence_number": "847891",
"has_active_loan": true,
"auto_repayment_triggered": true,
"timestamp": "2026-04-20T11:54:30Z"
}
}

Webhooks are delivered with at-least-once semantics. Your handler must be idempotent.

AttemptDelay after previous failure
1stImmediately
2nd30 seconds
3rd5 minutes
4th30 minutes
5th2 hours

After 5 failed attempts, the webhook is marked SUSPENDED and an alert is sent to the registered cooperative email. Reactivate via PATCH /webhooks/{webhook_id}.

Terminal window
# List webhooks
GET /webhooks
# Get webhook details + recent deliveries
GET /webhooks/{webhook_id}
# Update webhook URL or events
PATCH /webhooks/{webhook_id}
# Delete webhook
DELETE /webhooks/{webhook_id}
# Replay a specific delivery
POST /webhooks/{webhook_id}/replay/{delivery_id}
# Test webhook with a sample payload
POST /webhooks/{webhook_id}/test

Your endpoint must return 2xx within 10 seconds. If processing takes longer, return 200 immediately and process asynchronously:

app.post('/asilichain-webhook', async (req, res) => {
// Respond immediately
res.json({ received: true });
// Process asynchronously
setImmediate(async () => {
await processEvent(req.body);
});
});