Tag Manager

The Tag Manager lets you wire up integrations (sending events to third-party tools, modifying page content, firing pixels) without touching the customer's website. It's a Google Tag Manager replacement built on three primitives — Tags, Triggers, Variables — that compile to a single JSON file the tracker fetches alongside its main bundle.

Open at Web Analytics > Tag Manager inside a site.

The Three Primitives

Tags

A Tag is something that fires when a Trigger matches. Each Tag has a type that decides what it does:

Tag Type Behavior Common Use
event Emits a new event with a fixed name + custom properties built from variables. Promoting click[selector=#buy-now] to add_to_cart.
gtm-pixel Inserts an <img> or <script> pointing at an external endpoint. Facebook Pixel, Linkedin Insight, etc.
gtag-event Calls gtag('event', name, params) if a Google Analytics tag is on the page. Sending events to a parallel GA4 install during migration.
custom-html Injects arbitrary HTML into the page. Inline scripts customers can't safely deploy via their own CMS.
dataLayer-push Calls window.dataLayer.push(payload). Integrations that already use the GTM dataLayer convention.

Each Tag references one or more triggerIds. The Tag fires every time any of its Triggers matches.

Triggers

A Trigger is a rule that matches against the live event stream client-side.

Trigger Type Matches When Example
page-view A page_view event fires (optionally filtered by page_path regex). Fire on /pricing and all subroutes.
click A click event fires whose selector_path matches a CSS selector. Fire when #buy-now is clicked.
form-submit A form_submit event fires (optionally filtered by form_id / form_name). Fire when the signup form is submitted.
scroll-depth A scroll_depth event at a given threshold (25 / 50 / 75 / 100). Fire at 75% scroll.
custom-event A custom event with a specific name OR any event_kind === 'custom'. Fire on add_to_cart.

Triggers can have a list of conditions that filter further — equality, regex, contains, starts-with, etc. on any event field or any variable reference (e.g. variables.deviceType === 'mobile').

Variables

A Variable extracts a value from the event context or from the page DOM. Tags reference variables by {{variableName}} in their config.

Variable Type Source Notes
event-field event.<path> (e.g. properties.amount_usd). The most common type.
url-param URL(location.href).searchParams.get(name). Reads ?utm_source= and similar.
cookie document.cookie lookup. Use for first-party cookies; respects DNT/GPC.
js-expression A sandboxed JS expression evaluated with event + window in scope. Powerful but use sparingly — auditing is harder.
dom-selector document.querySelector(s).innerText. Pull a price out of the DOM at fire time.
data-layer Looks up a key in window.dataLayer. Bridges GTM-style sites.

Variables also have an optional defaultValue used when the source is missing or empty.

Workspaces

By default, every Site has one workspace named Main. Main is what the tracker actually runs.

For larger teams, create additional workspaces (e.g. Q3-conversion-experiment). Each workspace forks the current state of Main — Tags, Triggers, Variables, and all their cross-references. Edits inside a workspace don't affect the live tracker.

When you're done:

  1. Publish the workspace. This:
    • Deletes the existing Main Tags/Triggers/Variables.
    • Reparents the workspace's documents to Main (so cross-references stay valid by ObjectId).
    • Compiles the new container.
    • Increments Site.publishedContainerVersion and snapshots the old version for rollback.
  2. Archive the workspace if you don't expect to use it again. Archived workspaces stop showing in the dropdown but the documents are preserved.

Behind the scenes, workspaceService.ensureMainWorkspace() is idempotent and backfills any null-workspaceId documents to Main on first run, so older sites migrate automatically.

Versions & Rollback

Every publish snapshots the full container into the containerModel collection with a monotonically increasing version. Site.publishedContainerVersion points at the currently-live one.

To roll back:

  1. Open Tag Manager > Versions.
  2. Find the target version (each lists the publish actor, timestamp, and a diff summary).
  3. Click Restore — sets Site.publishedContainerVersion back. The tracker picks up the change on its next container fetch (cached 60 s with stale-while-revalidate=300).

Rollback is non-destructive — the workspace state isn't reverted, only the published snapshot. To also revert the workspace, fork from the restored version.

Preview Mode

To validate tag firing before publishing, use Preview Mode:

  1. Open the workspace you want to preview.
  2. Click Preview.
  3. Set the preview cookie (UI prints the exact karma_preview=<workspaceId>:<token> snippet) on the customer site.
  4. Visit the customer site. The tracker detects the cookie, loads the workspace's draft container instead of Main, and renders a Shadow-DOM overlay showing every event + every tag fire.
  5. The cookie expires after 24 h.

Preview mode is also where the AI Element Picker lives — see the next doc.

Publishing Flow

  1. Open a workspace.
  2. Edit Tags / Triggers / Variables (CRUD operations are scoped to the active workspace).
  3. Preview (optional but recommended).
  4. Publish — confirm in the modal. The compiled container goes live within ~60 s.
  5. The containerModel collection records who published, when, the version number, and a hash of the payload for tamper detection.

Anatomy of a Compiled Container

The compiled file served at /p/:siteKey/container.json:

{
  "payloadVersion": 1,
  "siteKey": "16efa189f624da8c",
  "version": 47,
  "variables": [
    { "id": "v1", "name": "pricePaid", "type": "event-field", "config": { "path": "properties.amount_usd" } }
  ],
  "triggers": [
    { "id": "t1", "name": "addToCartClicks", "type": "click", "config": { "selector": "#buy-now" } }
  ],
  "tags": [
    { "id": "tg1", "name": "fbConversion", "type": "gtm-pixel", "triggerIds": ["t1"], "config": { "url": "https://facebook.com/...", "params": { "value": "{{pricePaid}}" } } }
  ]
}

If the site has never published, the endpoint returns the same shape with empty arrays so the tracker can always proceed without a 404.

Common Patterns