Telemetry
Product analytics SQL recipe

Build a Signup-to-Activation Funnel in SQL

Calculate unique-user conversion through signup, onboarding, integration, and first-value milestones.

Intermediateproduct_eventsReviewed 2026-07-27

Question answered

Where do new users leave before reaching first value?

A funnel is only useful when every step has a behavioral definition. This query creates one row per user, enforces milestone order, and calculates conversion from signup and from the previous step.

Event contract

Fields the query expects

FieldTypeWhy it exists
timestamp_utcTimestampWhen the product event occurred.
user_idUtf8Stable user identifier.
event_nameUtf8Stable snake_case milestone name.
sourceUtf8Acquisition or campaign source.
DataFusion SQL

Copy the query

sql
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_orderstepusersconversion_from_signup_pctconversion_from_previous_pct
1Signed up1,240100
2Onboarded89271.9471.94
3Integrated60348.6367.6
4Activated44135.5673.13

Synthetic example output. Run the query against your own event schema and thresholds before using it for operational decisions.

How the SQL works

  1. 1The user_funnel CTE collapses repeated events into the first timestamp for each milestone.
  2. 2Timestamp comparisons enforce the intended order, so an out-of-sequence event cannot inflate a later step.
  3. 3The query reports both conversion from signup and conversion from the previous step, making total performance and the largest transition loss visible together.
  4. 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 setup

Put the recipe to work

Related instrumentation and guides

Continue the analysis

Run 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.

Get an API key