One line of code, zero maintenance.
The tracking snippet loads asynchronously, weighs ~6 KB compressed and doesn't modify any element on your page. It reads orders from your existing GA4 setup; if you prefer, you can send a signed webhook from your server.
<script async src="https://cdn.bannivo.com/bt.js" data-site="YOUR_SITE_KEY"></script> <!-- page type (optional) --> <body data-bt-page="home">
<div data-bt-slot="home-hero"> <a href="/campaign/winter"> <img src="/hero-winter.webp" alt="Winter sale"> </a> </div> <!-- banner image, link and name are read automatically -->
// if your site already has it, Bannivo reads it dataLayer.push({ event: 'purchase', ecommerce: { transaction_id: '100045', value: 1899.90, currency: 'TRY', items: [{ item_id: 'P100', quantity: 1 }] } });
BODY='{"order_id":"100045","value":1899.9, "visitor_id":"…","items":[…]}' SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 \ -hmac "$WEBHOOK_SECRET" | cut -d' ' -f2) curl -X POST \ https://cdn.bannivo.com/v1/webhooks/orders/YOUR_SITE_KEY \ -H "X-BT-Signature: $SIG" -d "$BODY"
What you need to know
What is tracked
Page views, banner discovery (image, link, position), viewable impressions, clicks, add-to-cart and orders. Clicks are sent with sendBeacon before the page changes.
Cookie consent
If Google Consent Mode is present, the analytics_storage signal is followed automatically. For your own consent banner, load with data-consent="false" and call bt.consent(true) on consent.
JS API
bt.getVisitorId() to add the visitor ID to webhooks, bt.track() for manual events, bt.refresh() for new content in single-page apps.
Data quality
Bots and automated traffic are filtered out, data is accepted only from domains you allow, and excessive requests from the same IP are rate-limited.
Performance
The snippet doesn't block page load; observation uses IntersectionObserver and keeps the main thread free.