Telemetry
Browse docs
GuidesUpdated July 27, 2026Reviewed by the Telemetry editorial and product teams3 min read

Use this doc with your coding agent

Open a focused prompt pack for Claude Code, Codex, Cursor, or another coding agent, then adapt it to the workflow covered here.

On this page
  1. 1. Define the funnel before adding events
  2. 2. Log a consistent event shape
  3. 3. Calculate the funnel in one query
  4. 4. Segment without changing the definition
  5. 5. Visualize and validate the result
  6. Next steps

Measuring Conversion Rates with SQL

An overall conversion rate tells you the result. Milestone events explain it by showing where users leave the funnel and which segment changed.

Four-step conversion funnel from landing page view through signup, checkout, and paid conversion

Adjacent-step conversion rates make the largest drop-off immediately visible.

This guide builds a user-level signup funnel in one event table and one SQL query. Counting distinct users at each stage avoids inflating conversion when one person repeats an action.

1. Define the funnel before adding events

Use milestones that represent completed outcomes, not button clicks that may fail:

  1. signup_completed
  2. workspace_created
  3. first_event_received
  4. dashboard_created

Choose one stable subject identifier such as user_id or team_id. The denominator and every later stage must use the same subject.

2. Log a consistent event shape

npm install telemetry-sh
import telemetry from "telemetry-sh";

telemetry.init(process.env.TELEMETRY_API_KEY);

await telemetry.log("product_events", {
  event_name: "signup_completed",
  user_id: user.id,
  team_id: team.id,
  acquisition_channel: "docs",
  plan: "free",
});

Log later milestones to the same product_events table with the same identifiers and segmentation fields:

await telemetry.log("product_events", {
  event_name: "first_event_received",
  user_id: user.id,
  team_id: team.id,
  acquisition_channel: user.acquisitionChannel,
  plan: team.plan,
});

Telemetry adds timestamp_utc automatically. Avoid logging emails, names, cookies, or raw request content when a stable internal identifier is sufficient.

3. Calculate the funnel in one query

The query below assigns one row per user, records whether each stage happened, and then calculates counts and conversion from the original signup cohort.

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 = 'workspace_created' THEN timestamp_utc END)
      AS workspace_created_at,
    MIN(CASE WHEN event_name = 'first_event_received' THEN timestamp_utc END)
      AS first_event_at,
    MIN(CASE WHEN event_name = 'dashboard_created' THEN timestamp_utc END)
      AS dashboard_created_at
  FROM product_events
  WHERE timestamp_utc >= now() - INTERVAL '30 days'
    AND event_name IN (
      'signup_completed',
      'workspace_created',
      'first_event_received',
      'dashboard_created'
    )
  GROUP BY user_id
),
stage_counts AS (
  SELECT 1 AS step, 'Signed up' AS stage, COUNT(*) AS users
  FROM user_funnel
  WHERE signed_up_at IS NOT NULL

  UNION ALL

  SELECT 2, 'Created workspace', COUNT(*)
  FROM user_funnel
  WHERE workspace_created_at >= signed_up_at

  UNION ALL

  SELECT 3, 'Sent first event', COUNT(*)
  FROM user_funnel
  WHERE first_event_at >= workspace_created_at

  UNION ALL

  SELECT 4, 'Created dashboard', COUNT(*)
  FROM user_funnel
  WHERE dashboard_created_at >= first_event_at
)
SELECT
  step,
  stage,
  users,
  ROUND(
    100.0 * users /
    NULLIF(MAX(CASE WHEN step = 1 THEN users END) OVER (), 0),
    2
  ) AS conversion_from_signup_pct,
  ROUND(
    100.0 * users /
    NULLIF(LAG(users) OVER (ORDER BY step), 0),
    2
  ) AS conversion_from_previous_pct
FROM stage_counts
ORDER BY step;

The first stage has no previous-stage rate, so conversion_from_previous_pct is null for that row. Every later row shows both total conversion from signup and adjacent-step conversion.

4. Segment without changing the definition

To compare acquisition channels or plans, preserve the field on the signup event, add it to user_funnel, and group the stage counts by that field. Do not silently switch the denominator from users to events.

Useful segment checks include:

  • acquisition channel;
  • initial plan or offer;
  • device or application surface;
  • signup week;
  • experiment assignment recorded before the outcome.

Avoid high-cardinality dimensions until the unsplit funnel is trustworthy.

5. Visualize and validate the result

Use a horizontal bar chart for users ordered by step, and display both rate columns in a result table. Before sharing the chart, inspect:

  • users completing a later stage before an earlier timestamp;
  • missing or reused identifiers;
  • internal, test, and bot accounts;
  • events that arrived late;
  • users whose signup occurred before the selected window;
  • whether the newest cohort has had enough time to convert.

A funnel is a cohort measurement, not simply two independent event counts. For longer conversion windows, cohort users by signup date and allow a fixed observation period.

Next steps

Open the complete signup activation funnel SQL recipe for an example result and visualization. Use weekly cohort retention once activation is defined, then compare feature adoption before upgrade.

Related product capability

Run read-only DataFusion SQL over structured-event tables and reuse the result.

Ownership and technical references

The Telemetry editorial team owns this explanation; the product team reviews behavior, examples, and boundaries.

Review the editorial standard