Tags, triggers & versions
The mental model is Google Tag Manager's: tags are things that fire, triggers decide when, and versionscontrol what's live. The difference is the vocabulary triggers speak — visitor type and agent identity are first-class fields.
Triggers
A trigger is a list of conditions, all of which must match (AND). Each condition is field · operator · value:
| Field | Meaning |
|---|---|
visitor_type | human, ai_crawler, ai_search, ai_assistant, ai_browser_agent, bot, unknown |
agent_name | Identified agent: GPTBot, ClaudeBot, ChatGPT-User, … |
agent_category | Same taxonomy as visitor_type, for identified agents |
referrer_source | AI platform that referred a human visit: chatgpt, perplexity, gemini, claude, … |
page_path | location.pathname, e.g. /pricing |
page_url | Full URL |
referrer | document.referrer |
context.<key> | Your site's window.context_data — see Context data below |
event_name | Custom event from aitm.track() — "" on plain page views |
Operators: equals, not_equals, contains, starts_with, regex, greater_than, less_than (numeric). A useful regex: match any AI visitor with visitor_type regex ^ai_.
Don't want to build conditions by hand? The trigger editor ships one-click quick setups for the common cases: Humans only (remove bots & AI from pixels and analytics), AI visitors only, AI-referred humans, and AI crawlers only. Click one, tweak if needed, save.
Context data — your input dataLayer
Declare page or business context beforethe snippet loads (the same pattern as Tealium's utag_data or GTM's dataLayer), and your triggers and tags can act on it:
<script>
window.context_data = {
page_type: "product",
brand: "acme",
logged_in: false
};
</script>
<script async src="https://visitortype.ai/tms.js" data-container="AITM-XXXXXXX"></script>Every key becomes available in two places: trigger conditions — pick Context data as the field and enter the key (stored as context.page_type) — and tag macros, as {{context.page_type}} (URL-encoded, like all macros). Example: fire a retargeting pixel only on product pages seen by humans: visitor_type equals human + context.page_type equals product.
window.aitm.context in the console.Even without setting anything, two keys are auto-detected: context.og_type (from your og:type meta tag — product, article…) and context.lang. On single-page apps, merge updates after route changes with aitm.setContext({ page_type: "checkout" }).
Custom events — aitm.track()
Fire tags at the moment something happens, not just on page load:
window.aitm.track("purchase", { value: 89, currency: "EUR" });Build the trigger with the Event name field: event_name equals purchase — plus anything else, e.g. visitor_type equals human and context.value greater_than 50(numeric operators are available for exactly this). The event's data merges into {{context.…}} macros, so a conversion pixel can carry {{context.value}}. Page-view tags never re-fire on events, and event tags never fire on page loads — the event_name field keeps them apart automatically. The result is worth saying plainly: conversion tracking that only counts verified humans.
Debugging — see why a tag did or didn't fire
Open any page with ?vt_debug=1 appended to the URL. A small panel shows the visitor classification (with the IP-verification verdict), the context the snippet read, every tag with a ✓/✗ match result, and each aitm.track()call as it happens. Remove the parameter and it's gone — it never renders for normal visitors.
Tag types
| Type | What it does | Config |
|---|---|---|
| Custom HTML | Injects HTML into the page; <script> tags execute | { "html": "..." } |
| Image pixel | Requests a 1×1 tracking image | { "url": "https://…" } |
| Data layer event | Pushes an event onto window.aitm.dataLayer | { "event": "…", "data": {} } |
A tag with no trigger fires on every page view.
Template catalog
25one-click templates generate the canonical vendor snippet from an ID — no copy-pasting from vendor docs. Each links to the vendor's official installation guide if you need to find your ID or go deeper. Pair every template with the Humans only trigger unless you have a reason not to.
| Analytics | Template fills in | Vendor docs |
|---|---|---|
| Google Analytics 4 | Measurement ID | Google ↗ |
| Plausible | Domain | Plausible ↗ |
| Matomo | Matomo URL, Site ID | Matomo ↗ |
| PostHog | Project API key, API host | PostHog ↗ |
| Fathom | Site ID | Fathom ↗ |
| Umami | Website ID, Script URL | Umami ↗ |
| Mixpanel | Project token | Mixpanel ↗ |
| GA4: AI channel source | Google ↗ |
| Paid media | Template fills in | Vendor docs |
|---|---|---|
| Google Ads Conversion | Conversion ID, Conversion label (optional) | Google ↗ |
| Meta Pixel | Pixel ID | Meta ↗ |
| LinkedIn Insight Tag | Partner ID | LinkedIn ↗ |
| X (Twitter) Pixel | Pixel ID | X ↗ |
| TikTok Pixel | Pixel ID | TikTok ↗ |
| Microsoft Ads UET | UET Tag ID | Microsoft ↗ |
| Pinterest Tag | Tag ID | Pinterest ↗ |
| Snap Pixel | Pixel ID | Snapchat ↗ |
| Reddit Pixel | Pixel ID | Reddit ↗ |
| Email & CRM | Template fills in | Vendor docs |
|---|---|---|
| Mailchimp | Connected Sites script URL | Intuit Mailchimp ↗ |
| Klaviyo | Public API key / Site ID | Klaviyo ↗ |
| Brevo | Client key (ma-key) | Brevo (ex-Sendinblue) ↗ |
| MailerLite | Account ID | MailerLite ↗ |
| HubSpot | Hub ID | HubSpot ↗ |
| Behavior | Template fills in | Vendor docs |
|---|---|---|
| Hotjar | Site ID | Hotjar ↗ |
| Microsoft Clarity | Project ID | Microsoft ↗ |
| Integrations | Template fills in | Vendor docs |
|---|---|---|
| Send visitor type to GTM | Google Tag Manager ↗ |
Already using Google Tag Manager? Make it AI-aware
You don't have to move anything. VisitorType can push its classification into GTM's own dataLayer, so every tag you already have in GTM can branch on visitor type — without rebuilding them here.
1. In VisitorType, add the Send visitor type to GTM template (Integrations) and leave its trigger empty — GTM needs the classification for every visitor so it can decide. Publish. It pushes:
window.dataLayer.push({
event: 'visitortype_ready',
visitor_type: 'human', // or ai_crawler, ai_search, ai_assistant, ai_browser_agent, bot
vt_agent_name: 'GPTBot', // when identified
vt_agent_category: 'ai_crawler',
vt_referrer_source: 'chatgpt' // AI platform that referred a human visit
});2. In GTM: create a Data Layer Variable named visitor_type, and a Custom Event trigger firing on visitortype_ready — optionally with the condition visitor_type equals human.
3. Switch any GTM tag you want protected (GA4, Meta Pixel, Google Ads…) from its All Pages trigger to that new trigger. Done: your existing GTM stack now fires for verified humans only, and you can build AI-specific tags in GTM the same way.
visitortype_ready simply fire a moment later than All Pages would — the trade for knowing what the visitor actually is.Versions & publishing
Saving tags and triggers never changes your site. When you hit Publish, the current set of enabled tags (with their trigger conditions resolved) is frozen into an immutable, numbered version — and that snapshot is what the snippet receives. Disable a tag and re-publish to remove it from the live config.
Recipes
Greet AI agents with structured pointers. Trigger: visitor_type regex ^ai_. Tag: Custom HTML injecting a note pointing at /llms.txt.
<div data-aitm-note style="display:none"> Canonical company facts: https://example.com/llms.txt Pricing: https://example.com/pricing — plans start at $29/mo. </div>
Keep human analytics clean. Trigger: visitor_type equals human. Tag: your GA4/Plausible snippet as Custom HTML — bots never inflate your numbers again.
Count assistant referrals. Trigger: visitor_type equals ai_assistant. Tag: Image pixel to your endpoint with ?agent={{agent_name}}&page={{page_path}}.