Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Guides exploration of $autocapture events captured by posthog-js to understand user interactions, find CSS selectors (especially data-attr attributes), evaluate selector uniqueness, query matching clicks ad-hoc, and create actions. Use when the user asks about autocapture data, wants to find what users are clicking, needs to build actions from click events, asks about elements_chain, wants to buil
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | 81% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 118% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 125% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 52% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 127% | 0% |
if users opt in then posthog-js automatically captures clicks, form submissions, and page changes as $autocapture events. Each event records the clicked DOM element and its ancestors in the elements_chain column.
$autocapture is intentionally excluded from the posthog:read-data-schema taxonomy because it is only useful with autocapture-specific filters (selector, tag, text, href). This skill fills that gap.
The events table provides fast access to common element fields without parsing the full chain string.
| Column | Type | Description | | ------------------------- | ------------- | ------------------------------------------------------------------------------------------------------ | | elements_chain | String | Full semicolon-separated element chain (see format reference) | | elements_chain_href | String | Last href value from the chain | | elements_chain_texts | Array(String) | All text values from elements | | elements_chain_ids | Array(String) | All id attribute values | | elements_chain_elements | Array(String) | Useful tag names: a, button, input, select, textarea, label |
Use materialized columns for exploration queries whenever possible — they avoid regex parsing.
Every $autocapture event from posthog-js ships with a fixed set of properties. Do not query the schema to "look them up" — they are these:
| Property | Examples | Notes | | ----------------- | --------------------------------- | ----------------------------------------------------------- | | $event_type | click, submit, change | the kind of interaction | | $el_text | Sign up, Submit | text of the clicked element | | $current_url | https://app.example.com/pricing | page the interaction happened on | | $elements_chain | semicolon-separated chain | parsed via the elements_chain* materialized columns above |
Standard event properties ($browser, $os, $device_type, etc.) are also present.
Run a count query before doing anything else. If the count is zero, autocapture may be disabled. There are two ways this happens:
autocapture_opt_out in PostHog project settingsinit() call can pass autocapture: falseTell the user if no data is found so they can check both settings.
sqlSELECT count() as cnt FROM events WHERE event = '$autocapture' AND timestamp > now() - INTERVAL 7 DAY
Start broad using the materialized columns. The goal is to understand what users are clicking before narrowing down.
Useful explorations:
elements_chain_elements)elements_chain_texts)elements_chain_href)elements_chain values for a specific page (filtered by properties.$current_url)See example queries for all patterns.
Once the user identifies an interaction they care about, find a CSS selector that identifies it.
Priority order for selector attributes (best first):
data-attr or other data-* attributes — highest specificity, stable across deploys, developer-intended anchors.Search with match(elements_chain, 'data-attr=') or extractAll.
attr_id) — also highly stable, queryable via elements_chain_ids.When a data-attr value is found, construct a selector like [data-attr="value"] or button[data-attr="value"].
A selector is only useful if it matches the intended interaction and not unrelated events.
Run a uniqueness check using elements_chain =~ with the regex pattern for the selector. Then sample matching events to inspect what the selector actually captures. Compare the count against total autocapture volume to understand selectivity.
A good selector matches a single logical interaction. If it matches too many distinct elements, refine it in the next step.
If the selector alone is not unique enough, layer on additional filters:
elements_chain_textsproperties.$current_urlelements_chain_hrefRe-run the uniqueness check after each refinement. Only include filters that are needed — fewer filters means more resilience to minor DOM changes.
When the user wants a funnel, trend, or other insight, the filter shape is different from HogQL. Each step in a FunnelsQuery / TrendsQuery is an EventsNode (or ActionsNode) with event: "$autocapture" and a properties array.
Two distinct property type values matter — they are not interchangeable:
type: "element" — keys: selector, tag_name, text, href. Matched against the parsed elements_chain. Operator support is split:selector and tag_name only support exact and is_not — anything else raises NotImplementedError in the query compiler (posthog/hogql/property.py).text and href accept the full string operator set (exact, is_not, icontains, not_icontains, regex, not_regex, is_set, is_not_set).type: "event" — keys: any of the canonical autocapture properties ($event_type, $el_text, $current_url) or anything else on the event. Standard event-property operators (exact, icontains, regex, etc.).Example funnel from clicking one button to clicking another:
json{ "kind": "FunnelsQuery", "series": [ { "kind": "EventsNode", "event": "$autocapture", "properties": [ { "type": "element", "key": "selector", "value": ["[data-attr=\"autocapture-series-save-as-action-banner-shown\"]"], "operator": "exact" } ] }, { "kind": "EventsNode", "event": "$autocapture", "properties": [ { "type": "element", "key": "selector", "value": ["[data-attr=\"autocapture-save-as-action\"]"], "operator": "exact" } ] } ] }
Two things easy to get wrong:
value is an array even when matching a single selector[data-attr="..."] wrapper — it is a CSS selector, not a bare attribute valueDecision rule: prefer an action (ActionsNode referencing an existing action — see Step 8) when the interaction will be referenced more than once; inline type: "element" / type: "event" filters when it's a one-off insight; raw HogQL (Step 7) when joining across events or doing custom aggregations.
The discovered selector can be used directly in HogQL without creating an action.
Trends — count matching clicks over time:
sqlSELECT toStartOfDay(timestamp) as day, count() as clicks FROM events WHERE event = '$autocapture' AND timestamp > now() - INTERVAL 14 DAY AND elements_chain =~ '(^|;)button.*?data-attr="checkout"' GROUP BY day ORDER BY day
Funnel — pageview to click conversion:
sqlSELECT person_id, first_pageview, first_click_after FROM ( SELECT p.person_id, p.pageview_time as first_pageview, min(c.click_time) as first_click_after FROM ( SELECT person_id, min(timestamp) as pageview_time FROM events WHERE event = '$pageview' AND timestamp > now() - INTERVAL 14 DAY AND properties.$current_url ILIKE '%/pricing%' GROUP BY person_id ) p INNER JOIN ( SELECT person_id, timestamp as click_time FROM events WHERE event = '$autocapture' AND timestamp > now() - INTERVAL 14 DAY AND elements_chain =~ '(^|;)button.*?data-attr="signup"' ) c ON p.person_id = c.person_id AND c.click_time > p.pageview_time GROUP BY p.person_id, p.pageview_time )
For recurring analysis, prefer creating an action (next step) or using posthog:query-trends / posthog:query-funnel with the action.
Actions are the durable version of ad-hoc selector queries. Once the criteria uniquely identify the interaction, create an action using posthog:action-create.
Construct the step with only the filters needed for uniqueness:
json{ "name": "Clicked checkout button", "steps": [ { "event": "$autocapture", "selector": "button[data-attr='checkout']", "text": "Complete Purchase", "text_matching": "exact", "url": "/checkout", "url_matching": "contains" } ] }
Available step fields for $autocapture:
selector — CSS selector (e.g. button[data-attr='checkout'])tag_name — HTML tag name (e.g. button, a, input)text / text_matching — element text (exact, contains, or regex)href / href_matching — link href (exact, contains, or regex)url / url_matching — page URL (exact, contains, or regex)After creation, verify with matchesAction():
sqlSELECT count() as matching_events FROM events WHERE matchesAction('Clicked checkout button') AND timestamp > now() - INTERVAL 7 DAY
$autocapture is high volumeLIMIT generously when sampling elements_chain — the strings can be longelements_chain =~ operator matches CSS selectors as regex internally;prefer materialized columns when possible for performance
Other measured skills in the registry, with their headline benchmark lift.