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
| Type | Fires when | Key payload fields |
|---|---|---|
payment.paid | A payment settles | payment_id, amount_cents, currency, fee_cents, merchant_ref, reference |
payment.expired | A code's 10-minute window closes unpaid | payment_id, merchant_ref |
payment.failed | The gateway reports a code unusable | payment_id, merchant_ref |
payout.confirmed | A payout lands in your InnBucks wallet | payout_id, amount_cents, currency, reference |
payout.failed | A payout is rejected โ your balance is auto-restored | payout_id, amount_cents, reason |
payout.reversed | A confirmed payout is reversed by the platform | payout_id, amount_cents |
withdrawal.redeemed | A customer claims a cash-out code at your till | withdrawal_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_typesdefaults to["*"](everything); list specific types to filter.- Endpoints are per mode โ register one with your test key and one with your live key.
GETlists endpoints,DELETE /v1/webhook_endpoints/:iddeactivates 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
});
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