adding-warehouse-perso…
Sync columns from a synced data warehouse table onto PostHog person or group properties, so warehouse data becomes usable anywhere person and group properties…
Get PostHog data into a canvas correctly: the host-injected `ph` SDK (loadInsight, query, capture, state, openExternal, navigate), the data hierarchy (saved insights first, typed query nodes second, inline HogQL last), verifiability (insight-backed metrics link their saved
$ npx -y skills add PostHog/ai-plugin --skill querying-canvas-data --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/querying-canvas-dataContext preview
The summary Claude sees to decide when to auto-load this skill.
Get PostHog data into a canvas correctly: the host-injected `ph` SDK (loadInsight, query, capture, state, openExternal, navigate), the data hierarchy (saved insights first, typed query nodes second, inline HogQL last), verifiability (insight-backed metrics link their saved
name: querying-canvas-data description: > Get PostHog data into a canvas correctly: the host-injected `ph` SDK (loadInsight, query, capture, state, openExternal, navigate), the data hierarchy (saved insights first, typed query nodes second, inline HogQL last), verifiability (insight-backed metrics link their saved insight in PostHog; ad-hoc queries expose the exact query that ran), per-insight-type result shapes, progressive per-query loading, date-range wiring, and event capture from a canvas. Use whenever a canvas shows metrics, charts, tables, or any PostHog data, or needs to send analytics events.
The `ph` bridge is the only way a canvas talks to PostHog. Import it with `import { ph } from "@posthog/canvas-sdk"` — a platform-provided module, so it needs no `dependencies` entry. The same object is also installed as the `window.ph` global, which existing canvases use; prefer the import in new code. Its typed surface is [references/canvas-sdk.d.ts](references/canvas-sdk.d.ts). Never initialize it: credentials stay in the host, and `fetch()`, posthog-js, and hand-rolled clients cannot reach PostHog from the sandbox. External requests and resources require a non-PostHog origin declared in `capabilities.network.origins`, and work only in the published canvas — the edit-mode preview blocks all direct network access. This includes external stylesheets; remote scripts remain blocked.
1. **Preferred — save an insight, load it by reference.** Use the PostHog MCP insight tools to create/save an insight that computes the metric with an insight query type (TrendsQuery, FunnelsQuery, RetentionQuery, PathsQuery, or the web-analytics kinds WebOverviewQuery / WebStatsTableQuery — not raw SQL). Confirm its numbers, note the `short_id`, and render it with `await ph.loadInsight(shortId, { dateRange })`. These are proven queries — numbers match the PostHog UI exactly (sessionization, unique users, breakdowns, bounce rate). Never fabricate a query or guess event/property names; discover and save them via MCP first. 2. **Secondary — an ad-hoc typed node**: `ph.query({ kind: "TrendsQuery", series: [...], dateRange: {...} })` when saving an insight genuinely doesn't fit. 3. **Last resort — inline HogQL**: `ph.query("SELECT …")`, only when no insight kind can express the metric; you then own the SQL and its date window.
For web-analytics boards specifically, use the web-analytics query kinds — raw HogQL subtly gets bounce rate, sessionization, channel attribution, and unique-visitor counts wrong.
Whatever tier you use, **declare it in the project's `capabilities`** before publishing: every `ph.loadInsight` short id in `capabilities.posthog.insights`, every `ph.capture` event name in `captureEvents`, and `inlineQueries: true` for any `ph.query` use. The host rejects undeclared calls at runtime, and validation fails on undeclared literals.
A number a viewer cannot verify is a number they cannot trust. Every data-backed figure a canvas shows — a KPI, a chart, a table, a stated conclusion — must carry the verification affordance for its tier:
1. **Insight-backed metrics link the real insight in PostHog.** For a metric loaded from a saved insight (the preferred tier), render a "View in PostHog" affordance that calls `ph.openExternal(insightUrl)` from a click. Mint the URL at authoring time with the `generate-app-url` MCP tool (path template `/insights/{id}` with the insight's short id) and bake the returned URL into the source verbatim — never hand-build one. `ph.openExternal` only opens `https://*.posthog.com` URLs and only from a user gesture, so wire it to a button or link, never to load or render. Do not also bake the insight's saved query text into the source: canvas source is readable by every canvas viewer, while access to the insight itself is enforced by PostHog — the link is where a viewer inspects the query, with their own permissions applied. 2. **Ad-hoc queries disclose the exact query that ran, viewable in place.** For a figure computed by `ph.query` (a typed node or inline HogQL), show the query behind it — the HogQL text, or the typed query node pretty-printed as JSON — in a modal or a collapsed disclosure attached to the card (a Quill `Dialog` or `Collapsible` in a React canvas, a `<details>` element in an HTML one). Render it from the same constant or builder you pass to `ph.query`, so the displayed query can never drift from the executed one. This discloses nothing beyond what the viewer already runs: `ph.query` executes as the signed-in viewer.
These are not optional polish: a canvas that presents PostHog data without them is incomplete. Keep the affordances compact — a small link icon per insight-backed card, a "View query" disclosure per ad-hoc card, or one shared modal listing every ad-hoc query the canvas runs, each labeled with the figure it backs.
For a status board, set `refresh` to the cache lifetime in seconds. Use a whole number from 30 to 86400 (one day); values outside that range, or fractional ones, fail at runtime:
await ph.loadInsight(shortId, { refresh: 30 })
await ph.query(queryNode, {}, { refresh: 30 })is an array of **series objects**, not rows. Each series has `data: number[]` (per interval), `days: string[]` (ISO), `labels: string[]`, `count` (sum), `aggregated_value` (single-value total), `label`, and optional `compare_label: "current" | "previous"`. A KPI total is `results[0].count` (or `.aggregated_value`); a line chart plots `results[0].data` over `results[0].days`. `count` sums the per-interval values, which double-counts a unique-users series (`ma
Official PostHog plugin for AI clients. Access PostHog products directly from your AI coding tool.
Repo: PostHog/ai-plugin
Sync columns from a synced data warehouse table onto PostHog person or group properties, so warehouse data becomes usable anywhere person and group properties…
Analyze the most expensive users in AI observability and explain why they cost so much. Use when the user asks about top spenders, expensive users, per-user…
Analyze session replay patterns across experiment variants to understand user behavior differences. Use when the user wants to see how users interact with…
Split a completed PostHog task run into activity records — what the agent tried, whether it worked, what blocked it — and record each one through the…
Assesses what a page's heatmap is telling you and recommends concrete changes. Pulls click / rageclick / scroll-depth data for a URL, names the hot elements by…
Audit every endpoint in a PostHog project for staleness, failed materialisations, and unused materialised versions. Use when the user asks "what endpoints can…