Event contract
Fields the query expects
| Field | Type | Why it exists |
|---|---|---|
| timestamp_utc | Timestamp | When the product event occurred. |
| user_id | Utf8 | Stable user identifier. |
| event_name | Utf8 | Stable snake_case milestone name. |
| source | Utf8 | Acquisition or campaign source. |
Copy the query
WITH user_funnel AS (
SELECT
user_id,
MIN(CASE WHEN event_name = 'signup_completed'
THEN timestamp_utc END) AS signed_up_at,
MIN(CASE WHEN event_name = 'onboarding_completed'
THEN timestamp_utc END) AS onboarded_at,
MIN(CASE WHEN event_name = 'integration_connected'
THEN timestamp_utc END) AS integrated_at,
MIN(CASE WHEN event_name = 'first_value_completed'
THEN timestamp_utc END) AS activated_at
FROM product_events
WHERE timestamp_utc >= now() - INTERVAL '30 days'
GROUP BY user_id
),
stage_counts AS (
SELECT 1 AS step_order, 'Signed up' AS step, COUNT(*) AS users
FROM user_funnel
WHERE signed_up_at IS NOT NULL
UNION ALL
SELECT 2, 'Onboarded', COUNT(*)
FROM user_funnel
WHERE onboarded_at >= signed_up_at
UNION ALL
SELECT 3, 'Integrated', COUNT(*)
FROM user_funnel
WHERE integrated_at >= onboarded_at
UNION ALL
SELECT 4, 'Activated', COUNT(*)
FROM user_funnel
WHERE activated_at >= integrated_at
)
SELECT
step_order,
step,
users,
100.0 * users
/ NULLIF(MAX(CASE WHEN step_order = 1 THEN users END) OVER (), 0)
AS conversion_from_signup_pct,
100.0 * users
/ NULLIF(LAG(users) OVER (ORDER BY step_order), 0)
AS conversion_from_previous_pct
FROM stage_counts
ORDER BY step_order;This recipe is read-only and reviewed against Telemetry's DataFusion query patterns. Sample output is deterministic and synthetic; validate field types, thresholds, and business definitions against your own data.
Query result
Signup-to-activation funnel
The largest absolute loss occurs before onboarding completion, while integration remains the strongest later bottleneck.
| step_order | step | users | conversion_from_signup_pct | conversion_from_previous_pct |
|---|---|---|---|---|
| 1 | Signed up | 1,240 | 100 | — |
| 2 | Onboarded | 892 | 71.94 | 71.94 |
| 3 | Integrated | 603 | 48.63 | 67.6 |
| 4 | Activated | 441 | 35.56 | 73.13 |
Synthetic example output. Run the query against your own event schema and thresholds before using it for operational decisions.
How the SQL works
- 1The user_funnel CTE collapses repeated events into the first timestamp for each milestone.
- 2Timestamp comparisons enforce the intended order, so an out-of-sequence event cannot inflate a later step.
- 3The query reports both conversion from signup and conversion from the previous step, making total performance and the largest transition loss visible together.
- 4The event names should represent completed milestones, not page views or button clicks that do not prove the user achieved the step.
Edge cases to decide
- Use a cohort window based on signup time and allow enough time for recent signups to activate.
- Events that arrive late can temporarily move users backward in a recently calculated funnel.
- Segment by acquisition source only after the total funnel has enough volume.
Recommended dashboard
- Funnel bars: users by step
- Line chart: activation rate by signup week
- Table: conversion by source, plan, or persona
Alert guidance
Use a low-urgency alert when activation for a mature signup cohort falls materially below its recent baseline.
Read alert setupPut the recipe to work
Related instrumentation and guides
Continue the analysis
Calculate Weekly Cohort Retention
Group users by first activity week and measure the percentage returning in later weeks.
Open recipeFind Features Used Before Upgrade
Join feature events to upgrade events and rank behaviors that occur before paid conversion.
Open recipeCalculate DAU, WAU, and Product Stickiness
Measure daily and weekly active users together and calculate DAU-to-WAU stickiness from a consistent activity definition.
Open recipeRun it on real events
Create a table, adapt the fields, and save the result
Start free, send structured events, and use the query result as a chart, shared dashboard widget, or alert input.