Compare self-serve vs sales-led user lifecycles

Split your funnel by how the user arrived — pricing-page vs demo. Compare activation, retention, expansion side by side.

Published

difficulty intermediate · time to value 5 minutes · tools used 3

Just say this

Split signups into self-serve (signed up via /pricing) vs sales-led (signed up after a demo). Compare their 30/60/90-day activation, retention, expansion.

Why this matters

Averaging across go-to-market motions hides everything. Self-serve and sales-led customers behave nothing alike:

  • Self-serve activates in days, churns fast, has tiny ACV, compounds via word-of-mouth.
  • Sales-led activates in weeks, churns slowly, has 30x ACV, requires a CSM.

A blended “30-day activation rate” tells you nothing useful about either. The PM looks at the number, sees 25%, doesn’t know whether to ship a self-serve onboarding fix or hire another CSM. Most dashboards never split the cohorts because nobody wired up the source signal at signup.

Agentry’s HogQL splits cohorts by source in one query and shows the two ICPs side by side. The strategic conversation gets unblocked.

What you get

  • Two cohorts (self-serve, sales-led) with their 30/60/90-day rates for activation, retention, upgrade
  • The metric where the gap is largest (= where the GTM playbook should differ most)
  • A public dashboard you can show the leadership team

Walk through it

You

Split signups into self-serve (signed up via /pricing) vs sales-led (signed up after a demo). Compare their 30/60/90-day activation, retention, expansion.

Agent

Building the two cohorts first.

agentry_create_cohort {
  project_id: "default",
  name: "self_serve_q1",
  description: "Signups that came through /pricing (no preceding demo event)",
  filters: [
    { event: "signup_completed", properties: { signup_path: "/pricing" }, since: "90d" }
  ]
}
{ "cohort_id": "coh_ss_1", "member_count": 1840 }
agentry_create_cohort {
  project_id: "default",
  name: "sales_led_q1",
  description: "Signups that completed a demo before signing up (within 30d)",
  filters: [
    { event: "demo_completed", since: "120d" },
    { event: "signup_completed", since: "90d" }
  ]
}
{ "cohort_id": "coh_sl_1", "member_count": 168 }
Agent

1,840 self-serve vs 168 sales-led. Now run the comparison.

agentry_analytics_query {
  project_id: "default",
  hogql: "WITH self_serve AS (SELECT distinct_id, min(timestamp) AS signup_ts FROM events WHERE event = 'signup_completed' AND properties.signup_path = '/pricing' AND timestamp >= now() - INTERVAL 90 DAY GROUP BY distinct_id), sales_led AS (SELECT distinct_id, min(timestamp) AS signup_ts FROM events WHERE event = 'signup_completed' AND distinct_id IN (SELECT distinct_id FROM events WHERE event = 'demo_completed' AND timestamp >= now() - INTERVAL 120 DAY) GROUP BY distinct_id), metrics AS (SELECT 'self_serve' AS cohort, count(DISTINCT s.distinct_id) AS n, countIf(DISTINCT s.distinct_id, exists(SELECT 1 FROM events e WHERE e.distinct_id = s.distinct_id AND e.event = 'first_project_created' AND e.timestamp < s.signup_ts + INTERVAL 30 DAY)) AS activated_30d, countIf(DISTINCT s.distinct_id, exists(SELECT 1 FROM events e WHERE e.distinct_id = s.distinct_id AND e.event = 'subscription_started' AND e.timestamp < s.signup_ts + INTERVAL 60 DAY)) AS paid_60d, countIf(DISTINCT s.distinct_id, exists(SELECT 1 FROM events e WHERE e.distinct_id = s.distinct_id AND e.event = 'plan_changed' AND e.properties.to_plan_rank > e.properties.from_plan_rank AND e.timestamp < s.signup_ts + INTERVAL 90 DAY)) AS expanded_90d FROM self_serve s UNION ALL SELECT 'sales_led', count(DISTINCT s.distinct_id), countIf(...), countIf(...), countIf(...) FROM sales_led s) SELECT * FROM metrics"
}
{
  "rows": [
    { "cohort": "self_serve", "n": 1840, "activated_30d": 920, "paid_60d": 184, "expanded_90d": 22 },
    { "cohort": "sales_led",  "n": 168,  "activated_30d": 134, "paid_60d": 134, "expanded_90d": 41 }
  ]
}
Agent

The contrast is dramatic:

self-servesales-led
n1,840168
activated 30d50%80%
paid 60d10%80%
expanded 90d1.2%24%

Volume is 11x on self-serve. Conversion is 8x better on sales-led. Expansion is 20x better on sales-led.

Strategic read: self-serve is correctly funded if your ACP is low and you want compounding. Sales-led is correctly funded if you can run a sales motion with that conversion. The MIDDLE — generic onboarding that treats them the same — is wrong for both.

Specifically: your sales-led activation gap (50→80%) is a sales-engineering job, not a product job. Self-serve’s pay-conversion gap (10% vs 80%) is the standard PLG funnel improvement, worth investing.

The output

Self-Serve vs Sales-Led — last 90d cohort

METRIC              SELF-SERVE    SALES-LED   RATIO   COMMENTARY
n (signups)              1,840          168    11.0x  Volume lives in self-serve
activated 30d              50%          80%   0.62x  Sales-led activates better
paid 60d                   10%          80%   0.13x  Sales-led converts 8x better
expanded 90d              1.2%          24%   0.05x  Sales-led expands 20x better

WHERE THE PLAYBOOKS DIVERGE:
  Self-serve activation:  50% → improve via in-product onboarding
  Self-serve conversion:  10% → improve via trial UX, pricing page
  Sales-led activation:   80% → already strong, hold the line
  Sales-led conversion:   80% → CSM quality issue, hire to match deal flow

IMPLIED ARR (rough, assuming self-serve $50/mo, sales-led $1,500/mo):
  Self-serve 60d revenue:    184 × $50    = $9,200/mo new
  Sales-led 60d revenue:     134 × $1,500 = $201,000/mo new
  → Sales-led drives 95% of revenue from 8% of accounts.

Strategic question: are we under-investing in sales given that ratio?

Setting it up

The only new requirement is knowing how each signup arrived. Capture it on the signup event:

// On signup completion, attach the path that brought them
await fetch(`https://api.agentry.sh/v1/analytics/${process.env.AGENTRY_PROJECT_ID}/`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.AGENTRY_DSN}`,
    "Content-Type": "application/json",
    "User-Agent": "myapp/1.0",  // REQUIRED — Cloudflare 403s default UAs
  },
  body: JSON.stringify({
    event: "signup_completed",
    distinct_id: user.email,
    properties: {
      signup_path: user.signup_path,  // "/pricing" | "/demo" | "/start" | etc.
      had_demo: user.last_demo_at != null,
      utm_source: user.utm_source,
    },
  }),
});

// Fire demo_completed from your demo-booking tool's webhook (Calendly, etc.)
await fetch(`https://api.agentry.sh/v1/analytics/${process.env.AGENTRY_PROJECT_ID}/`, {
  method: "POST",
  headers: { /* same */ "User-Agent": "myapp-calendly/1.0" },
  body: JSON.stringify({
    event: "demo_completed",
    distinct_id: bookingEmail,  // ← MUST match what they sign up with
    properties: { ae: bookingAssignee, demo_outcome: "qualified" },
  }),
});

If you can’t reliably fire demo_completed, an OK fallback is the signup-path heuristic alone (/pricing = self-serve, /contact-sales = sales-led).

Variations

  • “Same split but for last quarter only — has the ratio changed since we added the AE team?”
  • “Build the same comparison for 3 sub-cohorts: low ACV self-serve, mid-market sales-led, enterprise sales-led.”
  • “For sales-led only, compare activation rate by AE. Which reps are best at handoff?”
  • “For self-serve only, find users who didn’t activate in 30d but came back at 60d and DID activate. What did they do differently?”

Try this recipe in your own agent.

Paste the prompt above into your agent. It'll set up Agentry against your data, then run the recipe and show you the real output.

Install https://agentry.sh/agentry.md and set it up