Complete guide to installing and configuring the CRO9 behavioral tracker
Add this script tag before the closing </body> tag on any page you want to track:
<script
src="https://www.cro9.com/cro9-tracker.js"
data-api-key="YOUR_API_KEY"
data-endpoint="https://www.cro9.com/api/track/collect"
data-consent-mode="gdpr">
</script>| Attribute | Values | Default | Description |
|---|---|---|---|
| data-api-key | String | Required | Your unique CRO9 API key |
| data-consent-mode | gdpr, ccpa, essential, disabled | gdpr | Privacy compliance mode |
| data-endpoint | URL | https://www.cro9.com/api/track/collect | Where events are sent. Keep the www host — beacons cannot follow a redirect. |
| data-auto-conversions | true, false | true | Count every form submit as a conversion. Set false if you call CRO9.conversion() yourself. |
| data-debug | true, false | false | Enable console logging |
For single-page applications, use the Script component for proper loading:
import Script from 'next/script'
export default function Layout({ children }) {
return (
<html>
<body>
{children}
<Script
src="https://www.cro9.com/cro9-tracker.js"
data-api-key={process.env.NEXT_PUBLIC_CRO9_KEY}
data-endpoint="https://www.cro9.com/api/track/collect"
data-consent-mode="gdpr"
strategy="afterInteractive"
/>
{/* Route changes are tracked automatically (pushState is patched).
Apps that switch screens by state, not URL, call CRO9.page('/screen'). */}
</body>
</html>
)
}Once loaded, interact with the tracker via window.CRO9:
// Track a custom event
CRO9.track('button_click', {
buttonId: 'pricing-cta',
variant: 'A',
value: 99
});Form submits are counted automatically. Everything else that is worth money is a call you make on the page where it happened — first-party, from the visitor's own session.
// A goal reached — a booked call, a signup, a quote request.
// value and currency are optional; meta rides along into the event.
CRO9.conversion('booked_call', 250, 'USD', { source: 'calendly' })
// A sale, with the money on it. Deduped on transactionId, so a reloaded
// thank-you page never sells the same thing twice.
CRO9.purchase({
transactionId: order.id,
value: 39,
currency: 'USD',
plan: 'command',
items: [{ id: 'command_monthly', name: 'Command (monthly)' }],
})Connect your Stripe account on the Connections page and every settled charge, refund and dispute becomes a conversion on the session that produced it. Two metadata fields make the match exact; without them CRO9 falls back to the customer's email from identify().
// REVENUE TRUTH. Put the tracker's ids on the Stripe object you create,
// and CRO9 attributes the settled charge — and any refund — to the visitor
// who paid, once you connect Stripe on the Connections page.
const session = await stripe.checkout.sessions.create({
// …your line items…
metadata: {
cro9_vid: cro9VisitorId, // from CRO9.getVisitorId() on the page
cro9_sid: cro9SessionId, // from CRO9.getSessionId()
},
})// URL-driven apps need nothing: pushState / popstate are patched and
// every route change is a page_view. Apps that switch screens by state
// (tabs, wizards) report the screen themselves:
CRO9.page('/today')
CRO9.page('/settings/api', { section: 'keys' })// Identify a user (requires consent)
CRO9.identify('user_123', {
email: 'user@example.com',
name: 'John Doe',
plan: 'pro'
});// Listen for exit intent
window.addEventListener('cro9:exitIntent', () => {
// Show your exit popup
showExitPopup();
});See exactly what gets tracked in each consent state:
What CRO9 tracks in each consent state
Strictest compliance. No tracking until explicit consent.