Email click to first in-app action conversion

Join UTM-tagged email opens to the in-app action they were meant to drive. Grade onboarding emails by behavior change, not click rate.

Published

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

Just say this

Build a funnel from onboarding email opens (UTM-tagged) to the in-app action they were supposed to trigger. Per-template breakdown.

Why this matters

Lifecycle emails are graded by open rate, click rate, maybe unsubscribe rate. The metric that actually matters — “did they do the thing the email asked?” — requires joining your ESP data to your product data and almost no team does it.

Customer.io, Loops, Resend all give you “X people clicked.” None of them say “X people clicked AND THEN created their first project within 24 hours.” That join needs both sides in one database.

Agentry holds both: send every email open/click as an analytics event (one fetch from your ESP webhook), and join against your in-app events using distinct_id. The agent ships the funnel as a per-template scorecard.

What you get

  • A per-email-template funnel: sent → opened → clicked → in-app target action
  • Templates ranked by action-conversion rate (not click rate)
  • Surprise findings: the email everyone opens but no one acts on, vs the low-CTR email that drives 40% of activations

Walk through it

You

Build a funnel from onboarding email opens (UTM-tagged) to the in-app action they were supposed to trigger. Per-template breakdown.

Agent

Let me see what email events you’ve got and what target actions you’ve defined.

agentry_analytics_query {
  project_id: "default",
  hogql: "SELECT properties.email_template AS template, count() AS sent FROM events WHERE event = 'email_sent' AND timestamp >= now() - INTERVAL 30 DAY GROUP BY template ORDER BY sent DESC"
}
{
  "rows": [
    { "template": "welcome_day0",       "sent": 4200 },
    { "template": "create_first_project_day1", "sent": 4180 },
    { "template": "invite_teammate_day3",      "sent": 4100 },
    { "template": "feature_overview_day5",     "sent": 4050 },
    { "template": "trial_ending_day12",        "sent": 3800 }
  ]
}
agentry_run_recipe {
  recipe_id: "email_to_action_funnel",
  params: {
    project_id: "default",
    templates: [
      { name: "welcome_day0",              target_action: "signup_completed",          window_hours: 24 },
      { name: "create_first_project_day1", target_action: "first_project_created",     window_hours: 24 },
      { name: "invite_teammate_day3",      target_action: "team_member_invited",       window_hours: 48 },
      { name: "feature_overview_day5",     target_action: "integration_connected",     window_hours: 72 },
      { name: "trial_ending_day12",        target_action: "subscription_started",      window_hours: 72 }
    ],
    window_days: 30
  }
}
{
  "templates": [
    { "name": "welcome_day0",
      "sent": 4200, "opened": 2940, "clicked": 1188, "target_action": 1080,
      "open_rate": 0.70, "ctr": 0.40, "action_rate": 0.92 },
    { "name": "create_first_project_day1",
      "sent": 4180, "opened": 2090, "clicked": 418,  "target_action": 209,
      "open_rate": 0.50, "ctr": 0.20, "action_rate": 0.50 },
    { "name": "invite_teammate_day3",
      "sent": 4100, "opened": 2050, "clicked": 615,  "target_action": 41,
      "open_rate": 0.50, "ctr": 0.30, "action_rate": 0.067 },
    { "name": "feature_overview_day5",
      "sent": 4050, "opened": 1620, "clicked": 162,  "target_action": 162,
      "open_rate": 0.40, "ctr": 0.10, "action_rate": 1.00 },
    { "name": "trial_ending_day12",
      "sent": 3800, "opened": 2660, "clicked": 1140, "target_action": 380,
      "open_rate": 0.70, "ctr": 0.30, "action_rate": 0.33 }
  ]
}
Agent

Two findings worth your attention:

  1. invite_teammate_day3 is the worst. 615 people click, only 41 actually invite anyone. The CTA is broken — likely the click sends them to the workspace home, not to the invite modal. Fix the deep link.
  2. feature_overview_day5 is the surprise. Only 10% CTR but 100% of clickers do the target action. The email is filtering out un-interested users; those who click are pre-qualified. Don’t fix the CTR, leverage it.
  3. trial_ending_day12 has good open/click but only 33% conversion to subscription. Pricing or friction at checkout. Worth a separate investigation.
agentry_publish_query {
  recipe_id: "email_to_action_funnel",
  description: "Onboarding email → in-app conversion (30d)",
  params: { window_days: 30 }
}
Agent

Dashboard published. The actionable list: fix invite_teammate_day3’s deep link first.

The output

Onboarding Email Funnel (30d) — graded by ACTION, not click

TEMPLATE                     SENT   OPEN   CTR    ACTION   ACTION-RATE
welcome_day0                4,200  70%   40%   1,080         92%   ★ excellent
create_first_project_day1   4,180  50%   20%     209         50%   ok
invite_teammate_day3        4,100  50%   30%      41        6.7%  ✗ FIX
feature_overview_day5       4,050  40%   10%     162        100%  ★ pre-qualified
trial_ending_day12          3,800  70%   30%     380         33%  ✗ pricing/friction

KEY INSIGHTS:
  invite_teammate_day3 — 615 clicks → 41 invites. Deep link is broken.
                         Probably routing to workspace home instead of invite modal.
  feature_overview_day5 — 10% CTR is fine; 100% action rate means the CTA
                         self-selects. Don't optimize for opens.
  trial_ending_day12   — Click-to-paid conversion is only 33%. Friction at
                         checkout, not in the email. Investigate separately.

Setting it up

You need email events flowing in from your ESP, with distinct_id matching the in-app one (= user email). Most ESPs (Customer.io, Loops, Resend, Postmark, Sendgrid) ship webhooks — pipe those into a tiny handler that re-emits as Agentry analytics events.

// Webhook handler — e.g. Customer.io or Resend
app.post("/webhooks/email", async (req) => {
  const evt = req.body;
  // evt.event = "sent" | "opened" | "clicked"
  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-email-relay/1.0",  // REQUIRED — Cloudflare 403s default UAs
    },
    body: JSON.stringify({
      event: `email_${evt.event}`,  // → email_sent, email_opened, email_clicked
      distinct_id: evt.recipient,   // ← MUST match in-app distinct_id (= email)
      properties: {
        email_template: evt.template_name,
        utm_campaign:   evt.template_name,  // mirror for HogQL convenience
        link:           evt.link_url,       // for clicked events
      },
      timestamp: evt.timestamp,
    }),
  });
});

For in-app target_action events, you’re presumably already capturing first_project_created, team_member_invited, etc. for signup-funnel. The recipe just joins both sides on distinct_id within the time window.

Variations

  • “Same funnel but only for trial users on the Enterprise pricing path.”
  • “Compare onboarding email performance across two ESPs — we A/B’d Customer.io vs Loops last quarter.”
  • “For the worst-performing template, draft 3 subject-line / CTA variants based on what the winning ones do.”
  • “For each clicker who DIDN’T do the target action within 24h, send a follow-up. List them.”

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