PostHog Product Analytics Pipeline with BigQuery
1) Name your product's events
Note
Set the values now from what you know about your product. Several sections below also give you a query to check the value against real numbers - those read posthog_stage and posthog_reports tables that do not exist until Steps 4 and 5, so come back and run them once the pipeline has been through a full pass. The posthog_raw.events query just below is the only one in this step that works right now.
Three lists, all defaulted to a data-tooling company's events as a placeholder:
variables:
product_action_events:
type: array
default:
- pipeline_run_started
- query_executed
- dashboard_viewed
- asset_created
- lineage_explored
- connection_tested
- csv_exported
- team_member_invited
conversion_events:
type: array
default:
- signed_up
- subscription_started
upgrade_events:
type: array
default:
- subscription_started
product_action_events is the one that matters most. It drives product_actions, features_used, and the breadth and depth components of both the engagement score and the PQL score, so replace it with the eight-or-so events that mean someone got real value out of your product - not page views, not logins, the actions that are the point of using the tool. Find them from what you actually send:
bruin query --connection gcp-default --query "
SELECT event, COUNT(*) AS events, COUNT(DISTINCT distinct_id) AS distinct_users
FROM posthog_raw.events
GROUP BY event
ORDER BY events DESC
LIMIT 30
"
conversion_events sets sessions.converted. upgrade_events sets upgrade_rate in the feature-adoption report, which is what makes an experiment readout about revenue rather than about clicks. Leaving a list empty is fine - the predicate collapses to FALSE and the pipeline still runs, just with that column always false.
Warning
Both conversion_events and upgrade_events are frequently captured server-side rather than in the browser, which means they carry no $session_id. Once the staging layer exists in Step 4, posthog_stage.sessions.conversion_events_are_sessionized tells you whether this applies to your project - if it is FALSE for every session, converted will read false everywhere too, and that means "cannot be measured this way," not "nobody converted."