---
title: "PostHog Events — v2 Schema"
url: https://stacklist.com/card/f470b565-3f32-42ee-aaf2-7332b2a334bb
stack: https://stacklist.com/stack/b0e67eb4-097a-40c5-9866-dad59e9fe68b
summary: "PostHog Events v2 Schema is a governance document that defines the canonical list of analytics events the Stacklist frontend is allowed to emit, with strict naming conventions and categorization rules. It serves as the single source of truth for event management, synchronized with an Airtable database, and includes guidelines for adding new events and organizing them by audit verdict status."
tags: "analytics, event-schema, posthog, naming-convention, governance, product-events, v2-specification"
key_entities: "PostHog (technology), Airtable (technology), Stacklist (technology), Analytics v2 (concept), event-schema (concept), click-intent-funnels (concept), audit-verdict (concept)"
classification: "reference"
content_hash: "sha256:fce460a8105122b5669d0d0a4364a79ce908a5c957b0f17fd61fe0174f881a1f"
acp_version: "0.2"
token_counts_approximate: 5414
visibility: public
agent_accessible: true
status: "final"
---

# PostHog Events — v2 Schema

# PostHog Events — v2 Schema

> **Source of truth:** [Airtable Events table](https://airtable.com/appaxzLfLkGVdCWbZ/tblOZHByOBtbkQ9o7), filtered to `Status IN (Active, Planned (v2))`. This doc mirrors that view. If you change an event in code, update both the Airtable row and the matching row in this doc — see the PR checklist.

## Why this doc exists

Single governance anchor for the events the Stacklist frontend (and edge / server side) is allowed to emit in **Analytics v2**. Without a locked list, frontend devs add events ad hoc and the Airtable spec drifts from the codebase. The PR template now gates new events on an entry in both this doc and Airtable.

## How rows are grouped

Sections below are grouped by the Airtable **Audit verdict** field (added 2026-05-02 by the codebase ↔ PostHog audit). This is a more populated and meaningful split than the older `Verdict` field (which is mostly empty). The categorisation maps to FE-591's intent:

| Section | Audit verdict bucket | What it means |
| --- | --- | --- |
| Keep | `Keep` (or unset for canonical events) | Canonical product event we ship and rely on in v2. |
| Archive-but-Active | `Archive` | Marked for archive but kept live in PostHog by design (paired funnels, dormant tours that may re-launch, v1 events we still receive). |
| External system | `External system` | PostHog auto-captured, server-side pipeline, or third-party — not fired from app code. |
| Planned (v2) | `Add new` | In the v2 spec, not yet shipped (or just landing). |
| Consolidate | `Consolidate` | Slated to fold into another event; tracked separately. |

## v2 Naming convention

- **Actions** use `verb_noun` — `save_card`, `edit_stack`, `share_list`, `delete_account`.
- **State changes** use `noun_state` — `audio_uploaded`, `enrichment_completed`, `ghost_card_submitted`.
- **PostHog-reserved events** keep their leading `$` exactly as PostHog defines them — `$pageview`, `$ai_trace`, `$ai_generation`, `$ai_span`, `$identify`, `$exception`, etc. Do not rename or strip the `$`.
- **Click-intent pair preservation** — `click_<thing>` events exist alongside their completion counterparts on purpose. They form **click-intent funnels** (e.g. `click_featured_stack` → `view_stack`). **Do not archive a `click_*` event just because it "looks like a duplicate" of a completion event.** Check what funnel it feeds first. See the Archive-but-Active section for the current set kept live by design.
- **Display labels vs schema names** — keep the schema in snake_case; choose human-readable display labels in PostHog/Airtable separately. Example: `ai_referral_click` ships with display label "Identified AI Referrals".

## How to add a new event

1. **Add the row in Airtable first.** Set `Status = Active`, `Audit verdict = Keep` (or `Add new` if not yet shipped), fill in `Notes`.
2. **Add the constant** to `src/constants/analytics-events.ts` (use the `ANALYTICS_EVENTS` map; dynamic names go through helpers like `getFeaturedClickEvent`).
3. **Fire it** via the canonical helpers — `analytics.track(...)`, `analytics.hog(...)`, or `analytics.trackHogAndFathom(...)`. Always include workspace attribution when the event represents external engagement with owned content (see `useOrganizationAnalyticsContext` and the existing `save_card` / `like_*` / `share_*` / `follow` call sites for the pattern).
4. **Add the row to the matching section in this doc.** Keep alphabetical order within a section.
5. **Tick the PR checklist item** in your pull request.

## Out of scope (Phase B)

Properties documentation is **not** in this doc. Until the Phase B doc lands, reference the [Airtable Properties table](https://airtable.com/appaxzLfLkGVdCWbZ/tblcjyhTeI7w9qY89). Per-event properties are tracked there (1 row per event-property pair).

## Events

### Keep — canonical product events (76)

| Event | Status | Verdict | Replacement | Notes |
| --- | --- | --- | --- | --- |
| `ai_chat_conversation_started` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `ai_chat_error` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `ai_chat_message_sent` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `ai_chat_response_complete` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `ai_chat_tool_used` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `audio_uploaded` | Active | Keep | — | Code-fired event missing from Airtable inventory. Constant `AUDIO_UPLOADED` in `src/constants/analytics-events.ts`; fires when ghost-card source type is audio. Added 2026-05-02 by codebase audit. |
| `capsule_created` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `card_added_to_stack` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `click_auth_method` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `click_curator` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `click_discover_general` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `click_featured_card` | Active | Keep | `click_featured_stack` | Active in code; matched by call-site grep on 2026-05-02. |
| `click_featured_list` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `click_featured_stack` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `click_log_out` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `click_view_all_stack` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `copy_embedded_card` | Active | Keep | — | Code-fired event missing from Airtable. Dynamic event from share-modal Embed tab when `entityType=cards`. Sibling of `copy_embedded_stack` (already in inventory). Added 2026-05-02. |
| `copy_embedded_collection` | Active | Keep | — | Code-fired event missing from Airtable. Dynamic event from share-modal Embed tab when `entityType=lists` (which maps to `collection` in event name). Added 2026-05-02. |
| `copy_embedded_profile` | Active | Keep | — | Code-fired event missing from Airtable. Dynamic event from share-modal Embed tab when `entityType=users` (maps to `profile`). Added 2026-05-02. |
| `copy_embedded_stack` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `create_new_list` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `create_new_stack` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `cta_click` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `delete_account` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `delete_card` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `delete_data` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `delete_list` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `delete_stack` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `doc_uploaded` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `duplicate_card` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `duplicate_stack` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `edit_account` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `edit_card` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `edit_list` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `edit_stack` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `email_entered` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `email_log_in_success` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `email_sign_up_success` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `enrichment_completed` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `feature_list_on_profile` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `feature_stack_on_profile` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `follow` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `ghost_card_created` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `ghost_card_enriched` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `ghost_card_submitted` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `image_uploaded` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `like_card` | Active | Keep | — | RESOLVED 2026-05-02 (Kyle): keep success event (fires after mutation, global scope). Archive sibling `click_like_card`. Success event is the canonical signal. |
| `like_list` | Active | Keep | — | RESOLVED 2026-05-02 (Kyle): keep success event. Archive sibling `click_like_list`. Success event is the canonical signal. |
| `like_stack` | Active | Keep | — | RESOLVED 2026-05-02 (Kyle): keep success event. Archive sibling `click_like_stack`. Success event is the canonical signal. |
| `log_in` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `login_completed` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `open_card` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. Fires on external/public pages only by design (see `src/components/modals/card-details-modal/hooks/useDetailsModal.ts`). |
| `org_invite_accepted` | Active | Keep | — | Fires when a user accepts a workspace/org invite. Shipped in PR #1245. |
| `pdf_uploaded` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `plan_change` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `profile_status_updated` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `save_card` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. Carries owner `workspace_ids` (PR #1295). |
| `save_introduction` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `share_card` | Active | Keep | — | Consolidates `share_card_copy_text` and `share_card_social_links`. Carries `type: 'copy_text' / 'social_links'` to distinguish. `copy_embedded_card` is kept separate. PR `feat/consolidate-share-events` (2026-05-11). |
| `share_collection` | Active | Keep | — | Consolidates `share_collection_copy_text` and `share_collection_social_links`. Carries `type: 'copy_text' / 'social_links'`. `copy_embedded_collection` is kept separate. PR `feat/consolidate-share-events` (2026-05-11). |
| `share_profile` | Active | Keep | — | Consolidates `share_profile_copy_text` and `share_profile_social_links`. Carries `type: 'copy_text' / 'social_links'`. PR `feat/consolidate-share-events` (2026-05-11). |
| `share_stack` | Active | Keep | — | Consolidates `share_stack_copy_text` and `share_stack_social_links`. Carries `type: 'copy_text' / 'social_links'`. `copy_embedded_stack` is kept separate. PR `feat/consolidate-share-events` (2026-05-11). |
| `sign_up` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `sign_up_completed` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `unfollow` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `unlike_card` | Active | Keep | — | RESOLVED 2026-05-02 (Kyle): keep success event. `click_unlike_card` was never instrumented (no Airtable row, never fired in PostHog) — success event is the only signal. |
| `unlike_list` | Active | Keep | — | RESOLVED 2026-05-02 (Kyle): keep success event. `click_unlike_list` was never instrumented — success event is the only signal. |
| `unlike_stack` | Active | Keep | — | RESOLVED 2026-05-02 (Kyle): keep success event. `click_unlike_stack` was never instrumented — success event is the only signal. |
| `user_status_determined` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `video_uploaded` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `view_list` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `view_log_in` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `view_profile` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `view_sign_up` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `view_signup_pop_up` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |
| `view_stack` | Active | Keep | — | Active in code; matched by call-site grep on 2026-05-02. |

### Archive-but-Active — preserved for funnel/intent reasons (6)

> These rows are marked **Archive** in the audit but remain **Active** in PostHog by design. Some are v1 events we still receive; the rest are dormant integrations (Arcade Product Tour) that may re-launch. **Do not archive a row from this section without first checking what funnel/integration depends on it — they are load-bearing.**

| Event | Status | Verdict | Replacement | Notes |
| --- | --- | --- | --- | --- |
| `card_created` | Active | Archive | `create_new_card_click` | v1 `noun_verb`. Replaced by `create_new_card_click`. |
| `Flow Rendered` | Active | Archive | — | Arcade Product Tour. RESOLVED 2026-05-02 (Kyle): currently dormant. Archive in PostHog UI if needed — event definitions are reversible. If a tour re-launches, un-archive in PostHog and flip this row's Verdict to Keep. |
| `Hotspot Clicked` | Active | Archive | — | Arcade Product Tour. RESOLVED 2026-05-02 (Kyle): currently dormant. Archive in PostHog UI if needed — event definitions are reversible. |
| `Overlay Clicked` | Active | Archive | — | Arcade Product Tour. RESOLVED 2026-05-02 (Kyle): currently dormant. Archive in PostHog UI if needed — event definitions are reversible. |
| `Step Reached` | Active | Archive | — | Arcade Product Tour. RESOLVED 2026-05-02 (Kyle): currently dormant. Archive in PostHog UI if needed — event definitions are reversible. |
| `Video Ended` | Active | Archive | — | Arcade Product Tour. RESOLVED 2026-05-02 (Kyle): currently dormant. Archive in PostHog UI if needed — event definitions are reversible. |

### External system — auto-captured / bot / pipeline events (18)

> Captured by PostHog auto-capture, server-side pipelines, or third-party systems — not fired explicitly from app code. Listed here so future devs don't waste time looking for FE call sites.

| Event | Status | Verdict | Replacement | Notes |
| --- | --- | --- | --- | --- |
| `$autocapture` | Active | External system | — | PostHog auto-capture. Keep. |
| `$create_alias` | Active | External system | — | PostHog alias. Keep. |
| `$dead_click` | Active | External system | — | PostHog dead-click. Keep. |
| `$exception` | Active | External system | — | PostHog exception capture. Keep. |
| `$feature_flag_called` | Active | External system | — | PostHog feature-flag evaluation. Keep. |
| `$groupidentify` | Active | External system | — | PostHog group identify. Keep. |
| `$identify` | Active | External system | — | PostHog identify. Keep. |
| `$pageleave` | Active | External system | — | PostHog auto-captured pageleave. Keep. |
| `$pageview` | Active | External system | — | PostHog auto-captured pageview. Keep. |
| `$rageclick` | Active | External system | — | PostHog rage-click. Keep. |
| `$session_summary_ready` | Active | External system | — | PostHog AI session summary. Keep. |
| `$set` | Active | External system | — | PostHog `$set`. Keep. |
| `$web_vitals` | Active | External system | — | PostHog web vitals. Keep. |
| `ai_crawler_visit` | Active | External system | `bot_fetch` | AI/bot pipeline (server-side). Keep through Cycle 1, then consolidates into `bot_fetch` per v2 plan §4. Dual-Emit for 30d after Cycle 1 ship, then archive in PostHog. Dashboards query `bot_fetch` only during overlap window — legacy events emit but are NOT surfaced. |
| `ai_user_fetch` | Active | External system | — | AI/bot pipeline (server-side). Keep. |
| `bot_activity` | Active | External system | `bot_fetch` | AI/bot pipeline (server-side). Keep through Cycle 1, then consolidates into `bot_fetch`. Same Dual-Emit sunset as `ai_crawler_visit`. |
| `classic_crawler_visit` | Active | External system | `bot_fetch` | AI/bot pipeline (server-side). Keep through Cycle 1, then consolidates into `bot_fetch`. Same Dual-Emit sunset as `ai_crawler_visit`. |
| `unclassified_activity` | Active | External system | — | AI/bot pipeline (server-side). Keep. |

### Planned (v2) — not yet shipped (5)

> Defined in the v2 spec but not yet implemented (or just landing). Move a row to **Keep** once it is firing from code.

| Event | Status | Verdict | Replacement | Notes |
| --- | --- | --- | --- | --- |
| `$ai_generation` | Active | Add new | — | NEW v2. PostHog LLM Observability reserved schema. Fires server-side from `/api/ai/chat` — one per `anthropic.messages.stream()` round. Auto-captured by `withTracing` wrapper. Properties: `$ai_model`, `$ai_provider='anthropic'`, `$ai_input_tokens`, `$ai_output_tokens`, `$ai_cache_read_tokens`, `$ai_cache_creation_tokens`, `$ai_latency`, `round_index`, parent `$ai_trace_id`. `$groups` inherited from parent trace. Spec: `ANALYTICS_2.1.md §5.1.1`. |
| `$ai_span` | Active | Add new | — | NEW v2. PostHog LLM Observability reserved schema. Fires server-side from `/api/ai/chat` — one per `callMcpTool()` (manual instrumentation around `executeSingleTool`, NOT auto-captured). Properties: `tool_name`, `tool_input` (sanitized, max 4KB, PII-redacted), `tool_output_size`, `success`, `latency`, `error_type`, parent `$ai_trace_id`. Unlocks: which tools fail most, which are never used, retry rates per tool. Spec: `ANALYTICS_2.1.md §5.1.1`. |
| `$ai_trace` | Planned (v2) | Add new | — | NEW v2. PostHog LLM Observability reserved schema. Fires server-side from `/api/ai/chat` — one per HTTP request (whole agentic conversation). Use `posthog-node` SDK or PostHog `withTracing` Anthropic SDK wrapper. Properties: `$ai_trace_id`, `$ai_input`, `$ai_output`, `total_cost`, `total_latency`, `total_rounds`, `stop_reason`, `aborted`, `model`, `system_prompt_hash`, `$groups: { workspace }` from `req.body.context.workspaceId`. Bundle with: `ANTHROPIC_API_KEY → defineSecret` + Anthropic prompt caching on system block (~75% cost cut on repeat conversations). Spec: `ANALYTICS_2.1.md §5.1.1`. |
| `ai_referral_click` | Active | Add new | — | NEW v2. Display label = "Identified AI Referrals" (decouples snake_case schema from human-facing copy). Fires on `$pageview` when `document.referrer` host matches LLM map (chatgpt.com, claude.ai, perplexity.ai, gemini.google.com, copilot.microsoft.com, bing.com, you.com, qwen.ai, scira.ai, phind.com, meta.ai, grok.com/x.ai, mistral.ai, kagi.com, duckduckgo.com) per `classifyLLMReferrer` in `src/lib/analytics/llm-classify.ts`. Falls back to `$utm_source` if referrer stripped. Properties: `llm`, `referring_url`, `landed_path`, `content_id`, `content_type`, `owner_id`, `workspace_id`. Dashboard tooltip MUST document referrer-strip floor (lower-bound count). Spec: `ANALYTICS_2.1.md §5.3.1`. |
| `bot_fetch` | Planned (v2) | Add new | — | NEW v2. Replaces `ai_crawler_visit` + `bot_activity` + `classic_crawler_visit` (3 → 1). Emitted from Cloudflare Worker (edge) per plan §5.2.1. Carries Worker payload v2 fields: `bot_class`, `verified_bot_category`, `verified_bot`, `bot_name`, `bot_score` (Enterprise-only), `ja4` (Enterprise-only), `static_resource`, `detection_ids` (Enterprise-only), `cache_status`, `colo`, `tls_version`, `http_protocol`, `request_method`, `response_bytes`, `signature_agent/input/verified` (Web Bot Auth), `client_tcp_rtt`, `client_quic_rtt`, `delivery_rate`, `route_class`, `ingest_version='2.0'`. Attaches `$groups: { workspace, organization? }` at the edge. Spec: `ANALYTICS_2.1.md §5.2.1 + §5.2.2`. |

### Consolidate (1)

| Event | Status | Verdict | Replacement | Notes |
| --- | --- | --- | --- | --- |
| `team_plan_success` | Active | Consolidate | — | Plan-success consolidation handled by [FE-608](https://linear.app/stacklist/issue/FE-608). |

## References

- Airtable Events: https://airtable.com/appaxzLfLkGVdCWbZ/tblOZHByOBtbkQ9o7
- Airtable Properties (Phase B): https://airtable.com/appaxzLfLkGVdCWbZ/tblcjyhTeI7w9qY89
- Linear: [FE-591](https://linear.app/stacklist/issue/FE-591) (this doc) · [FE-590](https://linear.app/stacklist/issue/FE-590) (archive Marked-for-Deletion events) · [FE-608](https://linear.app/stacklist/issue/FE-608) (`team_plan_success` consolidation) · [FE-609](https://linear.app/stacklist/issue/FE-609)
- Initiative: [Stacklist Analytics v2.0](https://linear.app/stacklist/initiative/stacklist-analytics-v20-20f60c5353ef)
- Code constants: `src/constants/analytics-events.ts`
- Canonical analytics helpers: `src/utils/analytics.ts`, `src/analytics/posthog/posthog.ts`
- Org-attribution helper: `src/hooks/useOrganizationAnalyticsContext.ts`

