Install guide
Send the verdict to your analytics
Once our tag is on your pages, each pageview gets sent a verdict event: a person, an AI agent acting for a person, or a bot. How you use those verdicts is up to you, but the following is a guide on how to consume them. These recipes are not guaranteed and are subject to change as the analytics platforms themselves change.
Note that while Telltale Bot is designed to send verdicts as quickly as possible, there will also be some page loads that won't receive a verdict, meaning your own analytics platform will never report everything that our own reporting platform does.
Install the tag first (Install Telltale Bot). The recipes below read the verdict from the page events or, in a Tag Manager, from the dataLayer.
- How it works, whatever the tool
- Google Analytics 4, with Google Tag Manager or directly with gtag.js
- Plausible
- Matomo
- Piano Analytics
- PostHog, Mixpanel, Amplitude and Segment
- Any other tool, and other tag managers
How it works, whatever the tool
- Send the verdict as its own event. Your analytics tool records the pageview as the page loads. The verdict arrives a moment later, when our server answers. Each recipe sends an additional event that carries the verdict. Holding back the pageview until the verdict arrives is not recommended as it would lose the pageviews of visitors who leave first. In testing the median time from a page's initial start to when it receives a verdict is 1.4 seconds. The majority of this time (1s) is the page/tag manager waiting to load and run our script, not our functionality.
- Once per pageview. Each recipe sends the first verdict only. The tag can send a second verdict,
telltale:verdict-update, when the visitor's behavior changes it (for example when an AI agent shows itself with how it acts on the page). Sending that too counts the page twice in tools that count events. Our GA4 recipe sends both and tells them apart; the others leave the update out. - The snippet can go anywhere on the page. It listens for the verdict and also reads
Telltale.verdict, so it works whether it runs before or after the verdict arrives. Put it after your analytics tool's own snippet. - Fields. Each verdict has
actor(human,agenticorbot),agentId(who it is, such asclaude-extensionorgooglebot;humanfor people),confidenceanddeclared(see what the tag adds). Fields you add in Data layer fields come with it, such asagentName("Claude in Chrome"). - Per event, never per user. When an AI agent works in a person's own browser, your analytics tool sees that person's user. Store the verdict on the event (or the hit), not on the user or person profile, or that person would flip between
humanandagentic. - Expect fewer bots than in our viewer. Most analytics tools already filter out many bots, and automation that runs no JavaScript sends neither a pageview nor a verdict. What you see is the automation that got past your tool's own filtering, which is usually the interesting part.
- A pageview with no verdict event is most typically a bot, but it can be a person, so we don't call it either. No verdict means the tag never reported: a blocker or a company network stopped it, our server couldn't be reached, or the page closed before the answer came back.
- Consent. The tag puts the verdict in your page and nowhere else. The recipe sends it through your analytics tool, so your tool's own consent settings decide whether it goes further. See Consent.
Every recipe below has this shape, with the tool's own call inside send:
<script>
(function () {
var sent = false;
function send(v) {
if (sent || !v) return;
sent = true;
// the tool's own call goes here, with v.actor, v.agentId, ...
}
addEventListener('telltale:verdict', function (e) { send(e.detail); });
if (window.Telltale && Telltale.verdict) send(Telltale.verdict);
})();
</script>
Google Analytics 4
With our recipe each pageview sends GA4 one extra event, telltale_verdict, with these five parameters:
| Parameter | What it holds |
|---|---|
telltale_actor | human, agentic or bot |
telltale_agent_id | Who it is, such as claude-extension or googlebot; human for people |
telltale_confidence | high, medium or low |
telltale_declared | true when the visitor named itself, else false; empty ("(not set)") for people |
telltale_event | telltale.verdict for the first verdict, telltale.verdict-update when it changed after the visitor acted (most pages have none) |
It takes three steps: send the event, register its parameters in GA4 as custom dimensions (or you can't report on them!), and check that it arrives.
Step 1: send the event
Easiest, with Google Tag Manager: import our container. Installation in the viewer offers a GTM container with everything below already set up, our tag included (see import a container). You only fill in your GA4 measurement ID. It needs your GA4 Google tag in the same container.
Or set it up by hand in Google Tag Manager. Name everything as below, since the code refers to these names:
- Create four Data Layer Variables: Telltale Bot - actor for
telltale.actor, Telltale Bot - agentId fortelltale.agentId, Telltale Bot - confidence fortelltale.confidenceand Telltale Bot - declared fortelltale.declared. - Create a Constant variable, Telltale Bot - GA4 measurement ID, with your GA4 measurement ID (it starts with
G-). If you already have a variable for it, use yours in the code instead. - Turn on GTM's built-in Event variable (Variables, then Configure), if it is off.
- Create a Custom Event trigger with the event name
^telltale\.verdict(-update)?$and Use regex matching ticked. - Create a Custom HTML tag, Telltale Bot - GA4 verdict event, with the code below, on that trigger.
<script>
// Telltale Bot - GA4 verdict event: https://telltale.bot/analytics.html#ga4
// Sends each verdict to GA4 as a telltale_verdict event, through your GA4 Google
// tag in this container, at once rather than with gtag.js's next batch.
// It hands the event to gtag.js the way GTM's own GA4 tags do, after the GTM event
// that fired this tag (so after your consent defaults and the Google tag's setup),
// with a flag gtag.js reads as "send now". Google does not document that path: if
// it is missing, this sends a normal gtag event instead.
(function () {
try {
var name = {{Event}}, dl = window.dataLayer || [], eventId, i, m;
for (i = dl.length - 1; i >= 0; i--) {
m = dl[i];
if (m && m.event === name && typeof m['gtm.uniqueEventId'] === 'number') { eventId = m['gtm.uniqueEventId']; break; }
}
var params = {
send_to: {{Telltale Bot - GA4 measurement ID}},
telltale_actor: {{Telltale Bot - actor}},
telltale_agent_id: {{Telltale Bot - agentId}},
telltale_confidence: {{Telltale Bot - confidence}},
telltale_declared: {{Telltale Bot - declared}},
telltale_event: name
};
var args = (function () { return arguments; })('event', 'telltale_verdict', params);
var gtm = window.google_tag_manager, mb = gtm && gtm.mb;
if (eventId && mb && typeof mb.enqueue === 'function') {
mb.enqueue(args, eventId, { eventMetadata: { is_sgtm_service_worker: true } });
} else {
(window.dataLayer = window.dataLayer || []).push(args);
}
} catch (e) {}
})();
</script>
Why a Custom HTML tag and not a GA4 Event tag: a GA4 Event tag's event waits about 5 seconds for gtag.js's next batch, and a bot that closes the page first never sends it, so GA4 would undercount bots. This tag sends the event at once, through your Google tag, the way GTM's own GA4 tags do. Google does not document that way; if it ever changes, the tag sends a normal GA4 event instead (batched), so nothing breaks.
Or, without GTM, directly in the page with gtag.js. Add this after your gtag.js snippet. It sends the same event the same way, as long as the tag pushes the verdict to your dataLayer (on unless you turned it off); otherwise it goes with gtag.js's next batch.
<script>
(function () {
var sent = false;
function send(v, name) {
if (!v || typeof window.gtag !== 'function') return;
var params = {
telltale_actor: v.actor,
telltale_agent_id: v.agentId,
telltale_confidence: v.confidence,
telltale_declared: v.declared === undefined ? undefined : String(v.declared),
telltale_event: name
};
// Send it at once, not with gtag.js's next batch (about 5 seconds later), the way
// GTM's own GA4 tags hand gtag.js an event: once gtag.js has taken the tag's
// dataLayer message. Google does not document this: if it is missing, this sends a
// normal gtag event instead.
setTimeout(function () {
(window.dataLayer = window.dataLayer || []).push(function () {
try {
var dl = window.dataLayer, mb = window.google_tag_manager && google_tag_manager.mb, id, i, m;
for (i = dl.length - 1; i >= 0; i--) {
m = dl[i];
if (m && m.event === name && typeof m['gtm.uniqueEventId'] === 'number') { id = m['gtm.uniqueEventId']; break; }
}
if (id && mb && typeof mb.enqueue === 'function') {
mb.enqueue((function () { return arguments; })('event', 'telltale_verdict', params), id, { eventMetadata: { is_sgtm_service_worker: true } });
return;
}
} catch (e) {}
gtag('event', 'telltale_verdict', params);
});
}, 0);
}
function first(v) { if (!sent && v) { sent = true; send(v, 'telltale.verdict'); } }
addEventListener('telltale:verdict', function (e) { first(e.detail); });
addEventListener('telltale:verdict-update', function (e) { send(e.detail, 'telltale.verdict-update'); });
if (window.Telltale && Telltale.verdict) first(Telltale.verdict);
})();
</script>
Extra fields. If you chose Data layer fields, add a line for each next to the others: in the gtag.js code, such as telltale_isp_name: v.ispName; in GTM, a Data Layer Variable for it and a line such as telltale_isp_name: {{Telltale Bot - ispName}}. Register each one in step 2 too.
Step 2: register the parameters in GA4
GA4 keeps a parameter for reports only once it is registered. In GA4, go to Admin, then Custom definitions, and click Create custom dimension once for each of the five parameters: set Scope to Event and Event parameter to the parameter's name exactly as in the table above (you can give it the same name as its dimension name).
Use Event scope, never User: when an AI agent works in a person's own browser, GA4 sees that person's user, who would then flip between human and agentic.
Step 3: check it arrives
- In GTM, open Preview, load a page of your site and look for
telltale.verdictin the event list, with the Telltale Bot - GA4 verdict event tag fired. Then Submit the workspace. - In GA4, Realtime and DebugView show
telltale_verdictand its parameters at once. - Standard reports take a day or two. Dimensions fill in from the moment you register them, not before, so earlier days show "(not set)".
- Expect somewhat fewer verdict events than pageviews. A pageview with no verdict is most typically a bot that closed the page before the answer, but it can also be a person whose blocker or company network stopped our tag.
In GA4's reports
- The verdict is on the
telltale_verdictevent, not onpage_view. To see what bots and agents did on your site, use the segments below. - On a busy site, a report over several days broken down by a
telltale_*dimension can come back sampled and far off. Read one day at a time, use an exploration, or use BigQuery. - With Consent Mode and
analytics_storagedenied, the verdict event goes out the way your pageview does: without cookies, as a "cookieless ping". If your consent tool holds the Google tag back until consent, neither goes out until then. Your GA4 consent setup decides, not our tag. - Fields with many values (city, user agent, ISP name) collapse into "(other)" in large standard reports. Explorations and BigQuery suit them better.
Bots and agents as segments
Because the verdict is event-scoped, filtering a report on telltale_actor keeps only the verdict events, not the pageviews and other events of those visitors. To see everything automated visitors did, use a segment in an exploration (Explore, then Segments):
- Bots: a user segment. Include users when
telltale_actorexactly matchesbot. GA4 takes every user with at least one such event in the date range, then all of that user's events: pageviews, key events, purchases. It is the same rule our viewer uses for a visitor: the most automated of its sessions. - AI agents: a session segment. Include sessions when
telltale_actorexactly matchesagentic. Use sessions, not users: an agent that works in a person's own browser shares that person's GA4 user, so a user segment would count everything that person ever did as the agent's. - Updates count too. A page that started as
humanand turnedbotafter an interaction has a bot event, so the segment includes it. - Stricter. Add a second condition,
telltale_confidenceexactly matcheshigh. - In standard reports. Create an audience with the same condition (Admin, then Audiences) and compare against it. GA4 fills a new audience from recent data only (up to 30 days back), so for longer ranges use an exploration.
- Not a user-scoped dimension. Sending the actor as a user property as well would not help: a user property keeps only the latest verdict, and it is attached only to events sent after it is set, so it misses the pageview the visitor landed on, which is often a bot's only one.
Plausible
We run this one on our own sites. Add it after Plausible's snippet:
<script>
(function () {
window.plausible = window.plausible || function () { (plausible.q = plausible.q || []).push(arguments); };
var sent = false;
function send(v) {
if (sent || !v) return;
sent = true;
plausible('Telltale Verdict', {
props: { bot: v.actor, bot_agent: v.agentId },
interactive: false
});
}
addEventListener('telltale:verdict', function (e) { send(e.detail); });
if (window.Telltale && Telltale.verdict) send(Telltale.verdict);
})();
</script>
- In Plausible, Site settings, then Goals: add a Custom event goal named exactly
Telltale Verdict. Custom events don't show on the dashboard until a goal exists for them. - In Site settings, then Custom properties, add
botandbot_agent. Plausible ignores properties you haven't added there. (The names are yours to choose; keep them the same in both places.) - On the dashboard, open Goal conversions, click Telltale Verdict, then the Properties tab, and choose
botorbot_agentto see the split. interactive: falsekeeps the event out of bounce rate. Without it, every visitor who gets a verdict counts as engaged, and your bounce rate drops to near zero.- Plausible counts custom events toward your plan's monthly pageview allowance, so this about doubles what the pages with a verdict use.
- For readable names in the breakdown, turn on
agentNamein Data layer fields and sendbot_agent: v.agentName || v.agentId.
Matomo
Matomo records the verdict as an event (category Telltale Bot, action Verdict, name = the actor), and, if you set them up, in two custom dimensions you can use across Matomo's reports. Add it after Matomo's tracking code:
<script>
(function () {
var _paq = window._paq = window._paq || [];
var sent = false;
function send(v) {
if (sent || !v) return;
sent = true;
// Replace 1 and 2 with the IDs of your two action-scoped custom dimensions.
_paq.push(['trackEvent', 'Telltale Bot', 'Verdict', v.actor, undefined,
{ dimension1: v.actor, dimension2: v.agentId }]);
}
addEventListener('telltale:verdict', function (e) { send(e.detail); });
if (window.Telltale && Telltale.verdict) send(Telltale.verdict);
})();
</script>
- To use the custom dimensions, create two in Matomo (Administration, then Websites, Custom Dimensions) with the scope Action, not Visit, and put their IDs in the snippet. Without them, leave out the last argument: the event alone already gives the split under Behaviour, then Events.
- Matomo has no way to mark an event as non-interactive, so a visit with a verdict event no longer counts as a bounce, and its visit duration grows a little. Keep that in mind when you compare bounce rates from before and after.
- With Matomo Tag Manager instead of the tracking code, use the dataLayer events (below).
Piano Analytics
Piano Analytics records the verdict as its own event, telltale.verdict, with three event properties: telltale_actor, telltale_agent_id and telltale_confidence. Add it after Piano's tag (the JavaScript SDK, global pa):
<script>
(function () {
var sent = false;
function send(v) {
if (sent || !v || !window.pa || typeof pa.sendEvent !== 'function') return;
sent = true;
pa.sendEvent('telltale.verdict', {
telltale_actor: v.actor,
telltale_agent_id: v.agentId,
telltale_confidence: v.confidence
});
}
addEventListener('telltale:verdict', function (e) { send(e.detail); });
if (window.Telltale && Telltale.verdict) send(Telltale.verdict);
})();
</script>
- Piano processes only what your Data Model knows. In Data Management, then Data Model: under Events, create
telltale.verdict; under Properties, create the three properties as strings with the Event scope. Until then Piano lists them as "Unexpected" and ignores them, and a new event can take up to 20 minutes to go live. - Then break down any analysis by
telltale_actorortelltale_agent_id, filtered on thetelltale.verdictevent. To see everything automated visitors did, make a segment of visits that have atelltale.verdictevent withtelltale_actorequal tobot(oragentic). - Consent: in Piano's exempt (essential) mode, only the events and properties you have allowed for it are sent. To measure visitors who haven't consented, add the event and its three properties to your exemption configuration; otherwise the verdict goes only for visitors who opted in, like the rest of your custom events.
- Extra Data layer fields go in the same way, such as
telltale_agent_name: v.agentName, each declared as a property too. - With a tag manager instead of Piano's tag in the page, use the dataLayer events (below).
PostHog, Mixpanel, Amplitude and Segment
Product analytics tools take an event with properties. Each snippet goes after the tool's own snippet, which must be loaded under its usual global name (posthog, mixpanel, amplitude, analytics). Use event properties, not person, user or super properties (see per event, never per user).
PostHog
<script>
(function () {
var sent = false;
function send(v) {
if (sent || !v || !window.posthog) return;
sent = true;
posthog.capture('Telltale Verdict', {
actor: v.actor,
agent_id: v.agentId,
confidence: v.confidence
});
}
addEventListener('telltale:verdict', function (e) { send(e.detail); });
if (window.Telltale && Telltale.verdict) send(Telltale.verdict);
})();
</script>
Then filter or break down any insight by the Telltale Verdict event's actor or agent_id.
Mixpanel
<script>
(function () {
var sent = false;
function send(v) {
if (sent || !v || !window.mixpanel) return;
sent = true;
mixpanel.track('Telltale Verdict', {
actor: v.actor,
agent_id: v.agentId,
confidence: v.confidence
});
}
addEventListener('telltale:verdict', function (e) { send(e.detail); });
if (window.Telltale && Telltale.verdict) send(Telltale.verdict);
})();
</script>
Amplitude
<script>
(function () {
var sent = false;
function send(v) {
if (sent || !v || !window.amplitude) return;
sent = true;
amplitude.track('Telltale Verdict', {
actor: v.actor,
agent_id: v.agentId,
confidence: v.confidence
});
}
addEventListener('telltale:verdict', function (e) { send(e.detail); });
if (window.Telltale && Telltale.verdict) send(Telltale.verdict);
})();
</script>
Amplitude Browser SDK 2. With the older SDK, the call is amplitude.getInstance().logEvent(…).
Segment
<script>
(function () {
var sent = false;
function send(v) {
if (sent || !v || !window.analytics) return;
sent = true;
analytics.track('Telltale Verdict', {
actor: v.actor,
agent_id: v.agentId,
confidence: v.confidence
});
}
addEventListener('telltale:verdict', function (e) { send(e.detail); });
if (window.Telltale && Telltale.verdict) send(Telltale.verdict);
})();
</script>
Segment passes the event on to every destination you have connected, so this one recipe reaches them all.
Any other tool, and other tag managers
- A tool with a JavaScript event call: start from the shape above and put the tool's own event call inside
send. Send the verdict on an event, withactorandagentIdas its properties. - Tag managers that read the dataLayer (Matomo Tag Manager, Adobe Experience Platform Tags with a data layer extension, Tealium, Piwik PRO): use the
telltale.verdictdataLayer event as the trigger and readtelltale.actorandtelltale.agentIdfrom it, as in the GTM recipe. - Tools whose events take no properties: send one event name per actor, such as
Telltale human,Telltale agenticandTelltale bot.
The GA4 recipes have run with Google's own gtag.js in our tests, and the GTM one on customer sites; the Plausible one runs on our own sites. The others follow each tool's documentation. If one doesn't work for you, or you would like a recipe for a tool not listed, write to support@telltale.bot.