. Paste it into your site's in the one shared place all pages inherit — the layout file, base template or theme header. Site builders like Webflow, Framer or Squarespace have a custom-code box for exactly this."},{"@type":"HowToStep","position":3,"name":"Or install it through Google Tag Manager instead","text":"If your site already runs GTM and you would rather not touch code, open your container at tagmanager.google.com, create a new tag of type Custom HTML, paste the snippet as the tag's content, set the trigger to All Pages, then press Submit and Publish."},{"@type":"HowToStep","position":4,"name":"Deploy and watch the automatic events arrive","text":"Publish the change and open your site once in a normal browser tab. The three automatic events start on their own: page_view on every load and route change, session_start when a visit begins, session_end after thirty quiet minutes. The moment the first one arrives, the SiteOps SDK row in Settings flips to Connected — usually within a minute."},{"@type":"HowToStep","position":5,"name":"Tell SiteOps who people are with identify","text":"identify is the one standard event that is not a behaviour — it attaches traits to the current person. Call siteops(\"identify\", { plan: \"pro\" }) right after signup and login succeed, and again whenever a trait changes, such as an upgrade. From then on, audiences and rules can slice by those traits: an email to everyone on the free plan, a rule that only watches paying users."},{"@type":"HowToStep","position":6,"name":"Fire signup and activation at their moments","text":"Two lines cover the top of the lifecycle. Where the account is created — the handler that runs on success, or the page only new users reach — add siteops(\"track\", \"signup\"). Where your product delivers its first real value — the first report exported, the first spread drawn, the first video rendered — add siteops(\"track\", \"activation\"). Each fires once per person, at the moment itself."},{"@type":"HowToStep","position":7,"name":"Attach money to checkout","text":"Where a payment succeeds, add siteops(\"track\", \"checkout\", { value: 49 }) with the real order amount in place of 49. value must be a plain number; add currency as an ISO code like \"EUR\" if you charge in anything but USD. The value is what turns the checkout metric into revenue and gives ad attribution a number to credit campaigns with."},{"@type":"HowToStep","position":8,"name":"Track the shop funnel if you sell things","text":"Four commerce events cover a store, in the order shoppers move: siteops(\"track\", \"view_item\", { item: { id: \"deck-01\", price: 29 } }) on the product page, add_to_cart with the item and the line total, begin_checkout with the cart total, and siteops(\"track\", \"purchase\", { value: 58, items: [{ id: \"deck-01\", qty: 2 }] }) when the order completes. Each item is a plain object — id, and name, price, qty when you have them."},{"@type":"HowToStep","position":9,"name":"Name the company with group (B2B)","text":"If accounts belong to companies, call siteops(\"group\", { company_size: 120, industry: \"SaaS\" }) after login, next to identify. Like identify, it is not a behaviour — it attaches traits, at the company level. From then on cohorts can target by company_size or industry: an email to companies over 50 seats is account-based marketing in one line."},{"@type":"HowToStep","position":10,"name":"Name your own events well","text":"Anything the standard vocabulary misses becomes a custom event just by naming it: siteops(\"track\", \"spread_completed\") for a tarot app, \"report_shared\" for a SaaS, \"route_planned\" for a travel tool. Custom events are counted, charted and cohort-able exactly like the standard set. Keep names lowercase snake_case and stick to one name per action forever."},{"@type":"HowToStep","position":11,"name":"Verify every event in Settings","text":"Back in the SiteOps SDK row, the panel lists each event the snippet has ever sent with its count over the last thirty days. Walk your own lifecycle once — sign up with a test account, hit the activation moment, run a test payment — and watch each line appear. Anything you expected that stays at zero points at a call that never ran."}]}

Install the SiteOps SDK — your product's analytics memory

Every other connection shows SiteOps someone else's memory of your site: Google's count of your visits, Stripe's count of your money. The SiteOps SDK is your product's own analytics memory — one small script on your pages that remembers what people actually do there, first-hand, with no vendor in the middle and no approval to wait for.

It is also the engine's eyes, and that is what separates it from a passive counter. An AI marketing department is only as good as what it can see, and everything the SDK sees becomes something the engine can act on: the actions people take turn into audiences your emails and ads can target, into metrics your budget rules watch overnight, and into conversions your campaigns are credited with — value and all.

Like Amplitude or Mixpanel, the SDK has a real event taxonomy — a supported vocabulary rather than a free-for-all. Three automatic events fire themselves once the snippet is in. Nine standard events cover the lifecycle every product shares — identify, signup, activation, login, checkout, subscribe, unsubscribe, feature_used, search. Four commerce events cover the shop funnel — view_item, add_to_cart, begin_checkout and purchase with its items[] — and group() attaches company traits for account-based work. The whole engine understands every one of them without configuration; anything else you name becomes a custom event, counted just the same. The full table is below.

The install is a two-line snippet plus one line of code at each moment that matters: siteops("track", "signup") after the account is created, siteops("track", "checkout", { value: 49 }) after a payment. Ten minutes by hand, or one prompt to an AI agent — it is at the end of this guide and wires the whole taxonomy for you.

One line in the head, then it talks

Every event the SDK understands

Like any serious analytics tool, the SDK speaks a fixed vocabulary. Three automatic events fire themselves the moment the snippet is installed — you never call them. The standard events are the names the whole engine understands: audiences, rules, sequences and attribution all know what a signup or a purchase means without being told. Nine cover the lifecycle every product shares; four more are the commerce funnel — view_item, add_to_cart, begin_checkout and purchase, which carries the order's value and its items[]; and group() attaches company traits the way identify attaches personal ones, so cohorts can target by company_size or industry.

The vocabulary is open at the end — any other name you pass to siteops("track", …) becomes a custom event, counted, charted and cohort-able exactly like the standard set; lowercase snake_case names read best. And it is the same vocabulary on every platform: the iOS and Android SDKs speak identical event names — SiteOps.track("signup") in Swift or Kotlin is siteops("track", "signup") on the web, so cohorts and sequences never care where an event came from.

Every event the SDK understands

Quick links

What SiteOps reads

  • Only what the snippet sends: page views with their URL and referrer, the sessions they group into, the events you explicitly track with their properties, and the traits you pass to identify.
  • No form contents, no keystrokes, no recordings — an action you never named with a track call is invisible to SiteOps.
  • Events feed the dashboard metrics, audiences, budget auto-rules and ad conversion attribution. They are never sold or shared with the ad networks themselves.
  • The connection is one-way into SiteOps. The SDK never changes your pages, sets your prices or writes anything back to your site.
  • Rotating the site key stops new events immediately. Everything already collected stays on your charts until you remove the project.

Set it up, step by step

0 of 11 done

    1. Settings
    2. SiteOps SDK
    1. Tags
    2. New
    3. Custom HTML
    1. Settings
    2. SiteOps SDK
    3. Events

Let an agent do it

An agent with access to your site's code can install the snippet and wire the full taxonomy — identify, the lifecycle events, the money, the commerce funnel with its items, group() and a custom event — in one pass. Only generating the key stays with you.

Prompt for your agent
Install the SiteOps SDK on my site and wire its event taxonomy.

1. Add this snippet inside the <head> of every page — the site has a shared layout / base template, add it there once so all pages inherit it. If it is already present anywhere, do not add it a second time:
   <script async src="https://siteops.me/sdk.js" data-key="<my site key from SiteOps settings, starts with sk_sdk_>"></script>
2. Identify people. Right after signup and login succeed, call:
   siteops("identify", { plan: "<the user's plan or tier from our data model>" });
   Only include traits we already show the user (plan, name, email) — never secrets.
3. Where a new account is created (the success handler, or the page only new users reach), add once:
   siteops("track", "signup");
4. Find our product's first real value moment — the action that means a new user got what they came for — and add once:
   siteops("track", "activation");
   Tell me which moment you chose.
5. Where a payment succeeds, add, using the real order total and currency:
   siteops("track", "checkout", { value: <order total as a number>, currency: "<ISO code if not USD>" });
6. If the site sells products, wire the commerce funnel in shopper order:
   siteops("track", "view_item", { item: { id, name, price } }) on the product page;
   siteops("track", "add_to_cart", { item: { id, name, price, qty }, value: <line total> }) on the add action;
   siteops("track", "begin_checkout", { value: <cart total> }) when checkout starts;
   siteops("track", "purchase", { value: <order total>, currency: "<ISO code if not USD>", items: [<the real order lines as { id, name, price, qty }>] }) on order completion — purchase feeds revenue directly, so the items and value must be the real ones.
7. If accounts belong to companies, right after login also call:
   siteops("group", { company_size: <number>, industry: "<vertical>" });
   using what our data model actually knows — skip what it doesn't.
8. Pick the one action unique to this product that best signals engagement and track it as a custom event with a lowercase snake_case name, e.g.:
   siteops("track", "<action_name>");
9. The script is async and queues early calls, so normal page code is safe — just never call siteops() from code that runs before the document exists.
10. Load the site once, confirm the request to siteops.me/sdk.js succeeds with no console errors, then report every file you changed and where each call fires.

You still generate the sk_sdk_ key in your SiteOps settings and paste it into the prompt, and you confirm the events appear in the Settings panel — an agent cannot log into your dashboard for you.

If something does not work

I deployed the snippet but the status still says not connected
The status flips when the first event arrives, so something is stopping that event. Check three things in order: view your live site's page source and confirm the snippet is in the served HTML (not just in your editor); confirm the data-key matches the key in Settings character for character; and test with ad-blockers and private browsing off — blockers commonly stop analytics scripts, and a blocked test visit proves nothing. A visit from a normal browser tab should connect within a minute.
My single-page app only counts the first page
The SDK follows route changes through the browser's history API, which every mainstream router (Next.js, React Router, Vue Router) uses — so normally each route change fires its own page_view. If your app swaps content without touching the history, the SDK cannot see a change: either push a real history entry per view, or fire siteops("track", "page_view") yourself where the view switches.
My page view numbers look roughly double what Google Analytics shows
Almost always the snippet is installed twice — once in the layout and once in Tag Manager, or in two nested templates. Search your served page source for siteops.me/sdk.js and count the matches; there should be exactly one. Remove the extra copy and the counts settle from that point on. Historical days stay as recorded.
Checkout events arrive but show no revenue
The count without the money means value is missing or malformed. It must be a plain number — 49, not "$49" and not "49" as a string — passed as the third argument: siteops("track", "checkout", { value: 49 }). If you charge in something other than USD, add currency as an ISO code. Trigger a test payment and inspect the outgoing request in the network tab: the payload shows exactly what was sent.
Do I fire checkout or purchase when money comes in?
Both are standard and both carry value — they answer different shapes of business. checkout is the one-line payment moment, right for SaaS and services. purchase is the commerce order: a value plus items[], the real order lines, which is what makes funnel and best-seller math possible. A shop fires the four commerce events; a SaaS usually wants checkout and subscribe. Either way the value feeds revenue and ad attribution — and with no payment provider connected, purchase values are the revenue metrics themselves.
My event charts as two different metrics
Event names match by exact string, so "signup" and "Signup" — or "spread_completed" and "spreadCompleted" — are two separate metrics, each with half your data. Pick the lowercase snake_case form, search your codebase for every variant, and unify them. New events flow into the surviving name from the next deploy; the old line simply stops growing.

Set up once, see everything every morning

Get started