vTilt
By roleFoundersKnow what to fix firstMarketersSee what happens after the clickCMOsSee what your budget really buys
By businessSaaSTurn signups into active usersEcommerceSee why shoppers abandon cartsAgenciesProve your work to clientsEnterpriseOne customer record for every team
MeasureWeb AnalyticsFind where buyers leaveSession ReplaySee why they left, not just that they didAdsKnow what paid visitors do nextIntegrationsOne snippet feeds your stack
ThinkPeopleOne profile per customerCustomer MemoryvTilt remembers every visitorSite KnowledgeAnswers and audits from your own pages
ActAsk AIAnswers from real behaviorAI ChatChat that knows the visitorEmail CampaignsEmail from real visits
PricingWhy vTiltDocs
Docs / Autocapture
Getting Started
OverviewInstallInitializeIdentify usersTrack eventsLogout & resetVerify eventsCommon mistakes
Guides
Event forwardingReverse proxyRealtime dashboardSite knowledge
Frontend frameworks
Next.jsNuxt.jsVue.jsReactReact RouterRemixGatsbySvelte / SvelteKitAstroAngularTanStack StartDocusaurus
Backend frameworks
NestJSHonoCloudflare WorkersDjangoFlaskLaravelPhoenixRuby on Rails
Backend languages
PythonPHPRubyElixirGoJava.NET / C#Rust
Stack guides
Vue + PHP
API Reference
Browser SDK
Script bundlesAutocaptureWeb VitalsSession recordingChat widgetFeature readinessRemote configurationDebug logging
Node SDK
Install & setupCapture, identify & aliasContext & shutdownGlobal properties & opt-outError tracking
MCP server
Guides
OverviewAuthenticationOAuthAgent skills (prompts)AI intelligenceSite knowledgeGoogle AdsMeta Ads
Client setup
CursorClaude DesktopVS CodeCodex

Getting Started

OverviewInstallInitializeIdentify usersTrack eventsLogout & resetVerify eventsCommon mistakes

Guides

Event forwardingReverse proxyRealtime dashboardSite knowledge

API Reference

Script bundlesAutocaptureWeb VitalsSession recordingChat widgetFeature readinessRemote configurationDebug logging

MCP server

On this page

On this page

  • Enable autocapture
  • Debugging "no $autocapture events"
  • Events captured
  • Scroll depth
  • Properties collected
  • Advanced configuration
  • Privacy controls
  • Custom properties
  • Rage click detection
DocsAPI ReferenceBrowser SDKAutocapture

Autocapture

Last updated July 22, 2026

Capture clicks, form submits and input changes without manual instrumentation. Privacy-first defaults; full configuration.

Automatically capture clicks, form submissions, and input changes without manual instrumentation. Privacy-first by default — sensitive data is filtered and cookie consent banners are excluded from capture.

#Enable autocapture

Pass autocapture: true (or an AutocaptureConfig object) to vt.init to opt in. If you don't pass the option, autocapture is controlled by the Analytics → Autocapture toggle in your project settings — the SDK fetches that on boot via /decide and starts listening once it resolves.

vt.init('YOUR_PROJECT_TOKEN', {
  api_host: 'https://www.vtilt.com',
  autocapture: true,
})
typescript

Important

Setting autocapture: false in vt.init is final — the dashboard toggle cannot re-enable it on that page load. Leave the option unset if you want the dashboard to control it.

You can also start and stop autocapture programmatically at any time:

vt.startAutocapture() // takes precedence over the dashboard toggle
vt.stopAutocapture() // disables capture for the rest of the session
vt.isAutocaptureActive() // → boolean
typescript

#Debugging "no $autocapture events"

If pageviews are flowing but $autocapture events are missing, call vt.getAutocaptureDiagnostics() from the browser console. It returns a structured snapshot of the enabled state, the inputs that drove the decision, and a disabledReason so you can see at a glance why nothing is being captured (waiting on /decide, dashboard toggle off, server opt-out, or vt.stopAutocapture() was called).

vt.getAutocaptureDiagnostics()
// → {
//     isEnabled: false,
//     isStarted: false,
//     disabledReason: 'config_autocapture_undefined',
//     inputs: { configAutocapture: undefined, isDisabledServerSide: false, ... }
//   }
typescript

For richer logs, pass log_level: 'info' to vt.init — the SDK emits [vTilt]:autocapture lifecycle messages (started, stopped, disabled reason) on every config change.

Why is this specific element not captured?

When clicks are coming through but a particular button or link still doesn't fire $autocapture, set log_level: 'debug' and reproduce the click. The SDK prints a one-line trace per skipped event with the rule that rejected it:

[vTilt]:autocapture skip css_selector_ignorelist_hit { tag: "button", text: "Continue", target }
[vTilt]:autocapture skip vt-no-capture class on element chain { target }
[vTilt]:autocapture skip synthetic event (isTrusted=false) { type: "click", target }
[vTilt]:autocapture captured $autocapture { tag: "BUTTON", text: "Sign up", href: "/signup" }
text

The reason at the end of every skip line is one of:

ReasonWhat it means
no_window / no_elementThe event has no DOM target (rare — usually a synthetic event).
html_element / tag_htmlClick landed on the <html> element itself, not a real interactive child.
url_allowlist_missCurrent page URL didn't match autocapture.url_allowlist.
url_ignorelist_hitCurrent page URL is in autocapture.url_ignorelist.
dom_event_not_allowedEvent type (e.g. mouseover) isn't in autocapture.dom_event_allowlist.
element_allowlist_missTag (e.g. <div>) isn't in autocapture.element_allowlist.
css_selector_allowlist_missElement doesn't match any selector in autocapture.css_selector_allowlist.
css_selector_ignorelist_hitElement matches autocapture.css_selector_ignorelist (cookie banners by default).
form_event_not_allowedForm submit event for a tag that wasn't on the allowlist.
input_event_not_allowedInput change event for a non-capturable input.
tag_not_capturableElement tag isn't in the default capturable set (e.g. <span> outside a button).

Combine that with the existing vt-no-capture and vt-sensitive skip lines to fully account for every click that doesn't make it onto the wire.

#Events captured

EventTriggerConfig
$autocaptureClicks on links, buttons, inputs and other interactive elements; form submissions; input changes.autocapture: true (default)
$rageclick3+ clicks within 30 px in under 1 second.rageclick: true
$copy_autocaptureCopy/cut text events.autocapture.capture_copied_text: true
$scroll_depthUser crosses a scroll-depth milestone on the current page.autocapture.scroll_depth.milestones

$pageleave can include $prev_pageview_scroll_depth_pct when autocapture.scroll_depth.pageleave is enabled (requires capture_pageleave).

#Scroll depth

Two independently opt-in modes under autocapture.scroll_depth (both default off):

ModeConfigWhat you get
Milestonesmilestones: true or milestones: { thresholds: [50, 100] }$scroll_depth once per threshold per page
Page leavepageleave: trueMax scroll % on the page being left, on $pageleave
// Milestones only — funnel-friendly
vt.init('YOUR_PROJECT_TOKEN', {
  api_host: 'https://www.vtilt.com',
  autocapture: { scroll_depth: { milestones: true } },
})

// Page-leave summary only — lower volume
vt.init('YOUR_PROJECT_TOKEN', {
  api_host: 'https://www.vtilt.com',
  autocapture: { scroll_depth: { pageleave: true } },
  capture_pageleave: true,
})
typescript

A passive scroll listener runs when either mode is on. State resets on each $pageview (including SPA navigations). Milestone events respect url_allowlist and url_ignorelist like other autocapture.

#Properties collected

PropertyDescription
$event_typeDOM event type (click, submit, change).
$el_textVisible text of the element (buttons, links, nested spans).
$elements_chainEncoded chain string of element and ancestors (tag, classes, attributes, position).
$external_click_urlDestination URL when clicking an external link.
$selected_contentCopied/cut text (with capture_copied_text).
$copy_typeClipboard event type (copy or cut).
$el_valueForm field value on change (with capture_element_values, privacy-filtered).
$selected_textHuman-readable selection text — dropdown option text, checkbox label.
$element_selectorsMatched CSS selectors from action definitions.
$scroll_depth_pctMilestone threshold reached (milestones mode).
$scroll_depth_max_pctMax scroll depth % when the milestone fired.
$prev_pageview_scroll_depth_pctMax scroll depth % on the page being left (pageleave mode, on $pageleave).

#Advanced configuration

Pass an object to autocapture for fine-grained control over which pages, elements, and events are captured.

vt.init('YOUR_PROJECT_TOKEN', {
  api_host: 'https://www.vtilt.com',
  autocapture: {
    url_allowlist: ['https://example.com/app/.*'],
    url_ignorelist: ['https://example.com/admin/.*'],
    dom_event_allowlist: ['click', 'submit'],
    element_allowlist: ['a', 'button', 'form'],
    css_selector_allowlist: ['[data-track]', '.trackable'],
    css_selector_ignorelist: ['[data-no-track]', '#admin-panel'],
    element_attribute_ignorelist: ['data-secret', 'aria-label'],
    capture_copied_text: true,
    capture_element_values: true,
  },
})
typescript
OptionTypeDescription
url_allowlist(string | RegExp)[]Only capture on pages matching these URLs/patterns.
url_ignorelist(string | RegExp)[]Skip capture on pages matching these URLs (overrides allowlist).
dom_event_allowliststring[]Limit to specific event types (click, change, submit).
element_allowliststring[]Limit to HTML tags (a, button, form, input, select, textarea, label).
css_selector_allowliststring[]Only capture elements matching these CSS selectors.
css_selector_ignoreliststring[]Exclude elements matching these selectors (defaults exclude cookie banners).
element_attribute_ignoreliststring[]Exclude specific HTML attributes from captured properties.
capture_copied_textbooleanCapture text on copy/cut events as $copy_autocapture.
capture_element_valuesbooleanCapture form field values on change (opt-in, default false).
scroll_depth.milestonesboolean | { thresholds?: number[] }Milestone $scroll_depth events (default off).
scroll_depth.pageleavebooleanMax scroll % on $pageleave (default off).
scroll_depth.scroll_root_selectorstring | string[]Scroll container; defaults to window.

#Privacy controls

Use CSS classes to control what gets captured. Privacy protections apply automatically — password fields, sensitive names (cc, ssn, etc.), and credit card / SSN patterns in values are never captured.

<!-- Prevent autocapture on this element and its children -->
<div class="vt-no-capture">
  <button>This click will NOT be captured</button>
</div>

<!-- Mark element as containing sensitive data -->
<div class="vt-sensitive">
  <input type="text" placeholder="SSN" />
</div>

<!-- Force capture on a specific element -->
<span class="vt-include">This text WILL be captured</span>
html
ClassEffect
vt-no-capturePrevent capture on this element and all children.
vt-sensitiveMark as sensitive; only tag name, id, and class are captured (no text or input values).
vt-includeForce capture on an element that would otherwise be excluded.

Note

By default, autocapture excludes clicks on common cookie consent banners (CookieBot, OneTrust, Klaro, CookieYes, and many others). To disable this filtering, set css_selector_ignorelist: [].

#Custom properties

Attach custom key-value properties to autocapture events using data-vt-capture-attribute-* HTML attributes.

<button
  data-vt-capture-attribute-feature="signup"
  data-vt-capture-attribute-variant="blue"
>
  Sign Up
</button>

<!-- The autocapture event will include:
  { feature: "signup", variant: "blue", ...standard props } -->
html

#Rage click detection

Detect frustrated users clicking repeatedly in the same area. Fires a separate $rageclick event.

vt.init('YOUR_PROJECT_TOKEN', {
  api_host: 'https://www.vtilt.com',
  autocapture: true,
  rageclick: true,
})

// When a user clicks 3+ times within 30px
// in under 1 second, a $rageclick event fires.
typescript
PreviousScript bundlesBrowser SDKNextWeb VitalsBrowser SDK