Omni Way developer docs v1.0.0
Products ยท Webhooks & Events

Webhooks & Events

InnBucks itself has no push โ€” the platform converts everything that happens to your money into signed webhooks delivered to your server, with retries and a polling fallback. Fulfil on payment.paid and you never poll again.

Event types

TypeFires whenKey payload fields
payment.paidA payment settlespayment_id, amount_cents, currency, fee_cents, merchant_ref, reference
payment.expiredA code's 10-minute window closes unpaidpayment_id, merchant_ref
payment.failedThe gateway reports a code unusablepayment_id, merchant_ref
payout.confirmedA payout lands in your InnBucks walletpayout_id, amount_cents, currency, reference
payout.failedA payout is rejected โ€” your balance is auto-restoredpayout_id, amount_cents, reason
payout.reversedA confirmed payout is reversed by the platformpayout_id, amount_cents
withdrawal.redeemedA customer claims a cash-out code at your tillwithdrawal_id, amount_cents, currency

Register an endpoint

curl https://omniway.online/v1/webhook_endpoints \
  -H "Authorization: Bearer sk_test_..." -H "Content-Type: application/json" \
  -d '{"url": "https://yourshop.example/hooks/pos", "event_types": ["*"]}'

# โ†’ { "id": "โ€ฆ", "url": "โ€ฆ", "secret": "whsec_โ€ฆ" }   โ† secret is shown ONCE
  • Live endpoints must be https; in test mode http:// is allowed so local dev receivers work.
  • event_types defaults to ["*"] (everything); list specific types to filter.
  • Endpoints are per mode โ€” register one with your test key and one with your live key.
  • GET lists endpoints, DELETE /v1/webhook_endpoints/:id deactivates one.

Delivery shape

{
  "id": "7f3148d3-791d-492f-88a3-6530eb687597",
  "type": "payment.paid",
  "livemode": false,
  "created_at": "2026-06-11T12:08:21.000Z",
  "data": {
    "payment_id": "8c2b6668-โ€ฆ",
    "amount_cents": 2500,
    "currency": "USD",
    "fee_cents": 38,
    "merchant_ref": "order-43",
    "reference": "POSMB3K9TZ4X7Q2"
  }
}

Headers: X-Pos-Event-Id (the event id โ€” your dedupe key) and X-Pos-Signature (below).

Verify the signature

Every delivery is signed with your endpoint's whsec_ secret: X-Pos-Signature: t=<unix>,v1=HMAC_SHA256(secret, t + "." + rawBody).

const crypto = require("node:crypto");

function verifyPosSignature(rawBody, header, secret) {
  const m = /^t=(\d+),v1=([a-f0-9]+)$/.exec(header || "");
  if (!m) return false;
  if (Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false; // 5-min replay window
  const expected = crypto.createHmac("sha256", secret)
    .update(`${m[1]}.${rawBody}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(m[2]));
}

// Express: keep the RAW body for verification
app.post("/hooks/pos", express.raw({ type: "application/json" }), (req, res) => {
  if (!verifyPosSignature(req.body, req.get("x-pos-signature"), process.env.POS_WHSEC)) {
    return res.status(400).end();
  }
  const event = JSON.parse(req.body);
  if (event.type === "payment.paid") fulfilOrder(event.data.merchant_ref);
  res.status(200).end();           // 2xx fast โ€” do slow work async
});
โ–ฒ
Verify against the raw bytes

Parse-then-restringify changes the bytes and breaks the HMAC. Use express.raw() (or your framework's equivalent) on the webhook route only.

Retries & ordering

  • Anything but a 2xx within 10 s is retried with backoff: 1 m, 5 m, 30 m, 2 h, 12 h, then daily โ€” 8 attempts (~3 days) before the delivery is parked as dead.
  • Deliveries can arrive out of order and (rarely) more than once โ€” make your handler idempotent, keyed on X-Pos-Event-Id.
  • Respond 2xx first, process after. Slow handlers look like failures and get retried.

Polling fallback โ€” GET /v1/events

Can't host a public endpoint (desktop till, intranet)? The same events are pollable:

curl "https://omniway.online/v1/events?since=<last_event_id>&limit=100" \
  -H "Authorization: Bearer sk_test_..."

Events come back oldest-first; persist the last id you processed and pass it as since on the next poll. Every 30โ€“60 s is plenty.

Testing locally

# 1. run a local receiver on :4000, register it with your TEST key (http allowed in test mode)
# 2. create + instantly settle a sandbox payment:
PAY=$(curl -s https://omniway.online/v1/payments \
  -H "Authorization: Bearer sk_test_..." -H "Content-Type: application/json" \
  -H "Idempotency-Key: hook-test-1" -d '{"amount_cents":100}' | jq -r .id)
curl -X POST https://omniway.online/checkout/$PAY/mockpay
# โ†’ your receiver gets a signed payment.paid within ~5 seconds

Omni Way is the financial core; your storefront or till is the surface it powers. Integrate once and the same ledger, payouts and reporting work across every product you adopt.

omniway.online ยท InnBucksPowered by InnBucks (MicroBank Limited) ยท USD & ZWG.