ECG purchase & webhooks
End-to-end prepaid meter purchase with signed callbacks. Use sandbox keys for simulated fulfillment; production keys use live KNOS providers.
Flow
- Look up the meter with POST /meters/lookup.
- Create a purchase with
productIdfrom GET /products (stable codes such asecg-ecg). - Poll status or wait for webhooks. On success, read
tokenfrom the completed transaction / callback payload.
Create prepaid purchase
Scopes: transactions:create, ecg:purchase
POST
https://confident-elephant-309.convex.site/v1/transactionsRequest
curl -sS https://confident-elephant-309.convex.site/v1/transactions \ -H "Authorization: Bearer $KEY" \ -H "Content-Type: application/json" \ -d '{ "idempotencyKey": "ecg-order-001", "product": "ecg", "productId": "ecg-ecg", "amount": 50, "meterNumber": "0503123456", "meterId": "sbx-meter-1", "category": "PREPAID", "phoneNumber": "0241234567" }'201 Created
{ "transaction": { "id": "511260", "reference": "511260", "status": "pending", "kind": "purchase", "environment": "sandbox" }}Callback headers (actual)
KNOS signs the raw request body with HMAC-SHA256 and sends these headers on every delivery:
HTTP headers
X-Knos-Signature: <hex hmac-sha256 of raw body>X-Knos-Timestamp: <unix milliseconds>X-Knos-Delivery-Id: dlv_…X-Knos-Event: transaction.completed| Header | Meaning |
|---|---|
X-Knos-Signature | HMAC-SHA256 hex digest of the exact body bytes |
X-Knos-Timestamp | Unix time in milliseconds when the attempt was signed |
X-Knos-Delivery-Id | Stable delivery id (also in the payload) |
X-Knos-Event | Event name, e.g. transaction.completed |
Example completed payload
JSON body
{ "id": "dlv_…", "event": "transaction.completed", "timestamp": 1710000000000, "transaction": { "id": "511260", "reference": "511260", "status": "completed", "amount": 50, "currency": "GHS", "externalReference": null, "environment": "sandbox", "beneficiary": "0503123456", "meterNumber": "0503123456", "category": "PREPAID", "token": "1234-5678-9012-3456-7890", "customerMessage": null, "errorCode": null, "completedAt": 1710000001000 }}Field names are KNOS public names only — never upstream vendor identifiers. Sandbox may return a simulated token; meters ending in 0000 force failure for testing.
Verify signature
HMAC-SHA256 hex of the raw body using your webhook signing secret. Compare to X-Knos-Signature.
Node.js
import crypto from "node:crypto"; function verifyKnosSignature(rawBody, signatureHex, secret) { const expected = crypto .createHmac("sha256", secret) .update(rawBody) .digest("hex"); if (expected.length !== signatureHex.length) return false; return crypto.timingSafeEqual( Buffer.from(expected, "utf8"), Buffer.from(signatureHex, "utf8"), );} app.post("/hooks/knos", (req, res) => { const ok = verifyKnosSignature( req.rawBody, req.get("X-Knos-Signature"), process.env.KNOS_WEBHOOK_SECRET, ); if (!ok) return res.status(401).end(); res.status(200).end();});Test from the API
POST
https://confident-elephant-309.convex.site/v1/webhooks/testRequest
curl -sS -X POST https://confident-elephant-309.convex.site/v1/webhooks/test \ -H "Authorization: Bearer $KEY" \ -H "Content-Type: application/json" \ -d '{}'200 when URL + secret configured
{ "ok": true }Also see Webhooks overview and ECG & meters.