Outbound Webhooks & REST API
Event webhooks, scoped tenant API keys, and custom ERP connectors.
If your business uses an ERP other than Sage 200 Evolution (such as SAP Business One, Microsoft Dynamics 365, SYSPRO, or a custom warehouse backend), Kholo provides Outbound Webhooks and a Scoped REST API.
1. Tenant API Keys
External applications authenticate with Kholo using API keys.
Creating an API Key:
Navigate to Settings $\rightarrow$ API Keys or issue a key via REST:
POST /api/settings/api-keys
Authorization: Bearer <MANAGER_JWT_TOKEN>
Content-Type: application/json
{
"name": "Warehouse Scanner Integration"
}Response:
{
"id": "key-uuid",
"name": "Warehouse Scanner Integration",
"apiKey": "kholo_live_a89f3c7e0129bc45d7a6e124",
"createdAt": "2026-09-30T09:35:00.000Z"
}[!WARNING] The plaintext key is only returned once upon creation. Kholo stores only a cryptographic SHA-256 hash (
keyHash) in the database.
Authenticating with the API Key:
Pass the key in the HTTP request headers:
curl -H "X-Api-Key: kholo_live_a89f3c7e0129bc45d7a6e124" \
https://api.yourdomain.co.za/api/orders2. Subscribing to Outbound Webhooks
Kholo can dispatch real-time JSON webhooks to your server whenever order milestones occur.
Supported Events:
order.received: Fired immediately when a customer message or voice note is ingested.order.approved: Fired when all lines are verified and ready for ERP sync.order.completed: Fired when the order has successfully synced to your ERP.order.rejected: Fired if a clerk cancels an order.inventory.low_stock: Fired when an item's available stock drops below its reorder point.
Creating a Webhook Endpoint:
POST /api/settings/webhooks
Authorization: Bearer <MANAGER_JWT_TOKEN>
Content-Type: application/json
{
"url": "https://erp.yourcompany.co.za/api/kholo-receiver",
"secret": "whsec_custom_shared_secret",
"events": ["order.approved", "order.completed"]
}3. Webhook Payload Structure
When an event triggers (e.g. order.approved), Kholo sends an HTTP POST:
{
"event": "order.approved",
"timestamp": "2026-09-30T09:35:12.000Z",
"tenantId": "c4b12345-6789-abcd-ef01-23456789abcd",
"data": {
"orderId": "997220b1-3d2e-4fad-a758-fb0fe872aeb0",
"customer": {
"id": "cust-8832",
"erpCustomerId": "CUST-8832",
"name": "Clerk (KwaMashu Spaza)",
"phoneNumber": "+27 00 000 0000"
},
"totalAmount": 1937.85,
"lines": [
{
"sku": "WSTAR-12.5KG",
"description": "White Star Super Maize Meal 12.5kg",
"quantity": 10,
"unitPrice": 148.50,
"subtotal": 1485.00
},
{
"sku": "SUNF-OIL-2L",
"description": "Sunfoil Cooking Oil 2L",
"quantity": 6,
"unitPrice": 74.90,
"subtotal": 449.40
}
]
}
}Verifying Webhook Signatures:
Every outgoing request contains the header X-Kholo-Signature. Verify it in your server:
import crypto from 'crypto';
function verifyKholoWebhook(rawBody: string, signature: string, secret: string): boolean {
const hmac = crypto.createHmac('sha256', secret);
const digest = 'sha256=' + hmac.update(rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature));
}4. The Generic ERP Connector
If you configure your tenant's ERP type as generic_webhook:
{
"type": "generic_webhook",
"endpoint": "https://erp.yourcompany.co.za/api/sales-orders"
}Kholo acts as an intelligent front-end parser: it transcribes the customer's WhatsApp message, matches the catalog items, validates prices and stock, and then POSTs the clean order directly to your existing endpoint.