Rank errors by revenue impact, not raw count

Multiply users-affected by their plan ARR to surface the bugs costing real money. Stop triaging by event volume.

Updated · published

markdown for agents →

difficulty intermediate · time to value 2 minutes · execution on demand

Start from this

Show me open cases ranked by dollars-at-risk. For each, multiply users_affected by their plan ARR. Top 5.

Why this matters

Count-based triage is a trap. The loudest fingerprint in your error tracker is usually a cron loop hitting a stale row 5,000 times an hour — annoying, low-impact, fixable Tuesday.

Meanwhile the bug that three Pro accounts hit during checkout — twice each, total of 6 events — generated 80% of the revenue risk in that window. Sentry sorts it 80th on the list. You won’t see it until those three accounts churn.

The fix is to weight every error by the affected user’s plan value. Datadog won’t do this. Sentry won’t do this. The combination of error data + analytics data lives in one place in Agentry, so the agent can JOIN them in a single HogQL query and rank by dollars-at-risk instead of dumb count.

What you get

  • Top 5 open cases ranked by estimated ARR-at-risk (users-affected × per-user ARR)
  • For the #1 case: the specific accounts involved, with their plan tiers
  • A suggestion for who at your company should call those customers today

Walk through it

You

Show me open cases ranked by dollars-at-risk. For each, multiply users_affected by their plan ARR. Top 5.

Agent

Let me pull open cases with their affected user lists, then join against the latest plan value per user.

Endpoint: GET /v1/projects/:project_id/cases
Exact shape: /v1/openapi.json?path=%2Fv1%2Fprojects%2F%7Bproject_id%7D%2Fcases&method=get
Use query parameters, not a JSON body.
Query/filter concept:
  status: "open",
  since: "14d",
  include_affected_distinct_ids: true,
  limit: 25
{
  "cases": [
    { "id": "c_f72a", "message": "TypeError on user.plan", "users_affected": 14,
      "event_count": 89, "distinct_ids": ["[email protected]", "[email protected]", "..."] },
    { "id": "c_1cd9", "message": "fetch failed: invoice.pdf", "users_affected": 9,
      "event_count": 11, "distinct_ids": ["..."] },
    { "id": "c_2d8e", "message": "checkout 500: stripe webhook timeout", "users_affected": 3,
      "event_count": 6, "distinct_ids": ["[email protected]", "[email protected]", "..."] }
  ]
}
Agent

Now let me join the affected distinct_ids against current plan tier.

Endpoint: POST /v1/projects/:project_id/analytics/query
Exact shape: /v1/openapi.json?path=%2Fv1%2Fprojects%2F%7Bproject_id%7D%2Fanalytics%2Fquery&method=post
Custom HogQL goes in the OpenAPI-defined `query` field.
Concept fields:
  query: "SELECT distinct_id, argMax(properties.plan, timestamp) AS plan FROM events WHERE distinct_id IN ({ids}) GROUP BY distinct_id",
  params: { ids: ["[email protected]", "[email protected]", "[email protected]", "[email protected]", "..."] }
{
  "rows": [
    { "distinct_id": "[email protected]", "plan": "free" },
    { "distinct_id": "[email protected]", "plan": "starter" },
    { "distinct_id": "[email protected]", "plan": "enterprise" },
    { "distinct_id": "[email protected]", "plan": "scale" }
  ]
}
Agent

Ranked by ARR-at-risk (assuming starter=$300, scale=$3,000, enterprise=$30,000):

  1. c_2d8e — checkout 500 on Stripe webhook timeout — $63,000 ARR at risk Only 6 events but it’s hitting 2 enterprise + 1 scale account. This is your #1.
  2. c_f72a — TypeError on user.plan — $4,200 ARR 14 users but 11 are on free tier (the bug literally fires on the free-tier path).
  3. c_1cd9 — invoice PDF fetch — $2,700 ARR 9 users, mostly starter.

The numbers flip completely. c_2d8e is 15x more important than c_f72a by revenue, but raw count says the opposite. Want me to pull the case detail on c_2d8e and draft a Slack message to whoever owns Stripe?

The output

Top 5 cases by ARR-at-risk (14d, open)

$63,000   c_2d8e   checkout 500: stripe webhook timeout
                   3 users  · 6 events
                   Affected: [email protected] (enterprise $30k),
                             [email protected] (scale $3k),
                             [email protected] (enterprise $30k)
                   ↗ Owner: @stripe-oncall — investigate webhook latency

 $4,200   c_f72a   TypeError on user.plan (free-tier path)
                   14 users · 89 events · mostly free tier
                   ↗ Low ARR, but fixable in 4 lines

 $2,700   c_1cd9   invoice.pdf fetch failed
                   9 users · 11 events · mixed plans
                   ↗ Probably CORS regression on invoice-service

   $600   c_a3e1   phone validation NANP-only
                   7 users · 14 events · all starter
                   ↗ Low priority unless we want international growth

   $300   c_4f01   pagination edge case
                   2 users · 3 events · starter
                   ↗ Easy fix, ship next sprint

Net ARR at risk: $70,800

Setting it up

The workflow joins two things: cases (your error data) and events (your analytics data, where plan lives). For the join to work, both must use the same distinct_id.

// Error reporting — distinct_id (or user.id) MUST match analytics
await fetch(`https://api.agentry.sh/v1/logs/`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.AGENTRY_SERVER_API_KEY}`,
    "Content-Type": "application/json",
    "User-Agent": "myapp/1.0",  // REQUIRED — Cloudflare 403s default UAs
  },
  body: JSON.stringify({
    message: err.message,
    stack: err.stack,
    user: { id: currentUser?.email },  // ← same id as analytics
  }),
});

// Analytics — every event carries `plan` so we can JOIN later
await fetch(`https://api.agentry.sh/v1/analytics/`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.AGENTRY_SERVER_API_KEY}`,
    "Content-Type": "application/json",
    "User-Agent": "myapp/1.0",
  },
  body: JSON.stringify({
    event: "feature_used",
    distinct_id: currentUser.email,  // ← same id as errors
    properties: { plan: currentUser.plan, mrr: currentUser.mrr },
  }),
});

If you don’t track plan on every event, an alternative is to fire a one-shot plan_changed event whenever it changes — the HogQL argMax(properties.plan, timestamp) pattern picks up the latest value either way.

Variations

  • “Same ranking, but only count enterprise customers (plan = ‘enterprise’).”
  • “Show errors ranked by ARR-at-risk over the last 24 hours, not 14 days. Daily oncall summary.”
  • “For the top case, draft a per-customer outreach email apologizing and offering credit.”
  • “Run this on demand for the last business day and draft the top three for an authorized CS reviewer; do not expose affected emails automatically.”

Adapt this workflow in your own agent.

Ask your agent to map the starter prompt to your saved signal map, live events, and the current HTTP API before answering.

Agentry gives coding agents production context from errors, product analytics, and deploys through one HTTP API. Fetch and read https://agentry.sh/install.md completely, then install Agentry's full first-time telemetry baseline in this repository. I authorize device-code login, saving the owner key only to ~/.agentry/credentials.json, creating one Agentry project for this repository, and read-only inspection of shipped source plus hidden CI/provider configuration. Deeply analyze the product's business logic and complete value flow before choosing telemetry. Save the complete source-backed plan and show me its exact contents and hash. Until I explicitly approve that plan or request a replacement, do not edit app/CI source, place runtime/CI credentials, commit, exercise product traffic, start proof, push, or deploy. After approval, I authorize only the reviewed targets: place the required scoped browser/server/CI credentials through the established environment or secret mechanism, preserve existing telemetry, implement and test the baseline, commit it, push that reviewed commit when the shipped CI/provider path requires it, exercise safe proof paths with test/non-customer data, and perform one deployment through the reviewed shipped CI/provider path. Ask first if proof would charge money, contact a third party, change real customer data, or require new external access. After the plan is saved, immediately before every onboarding state-changing POST, GET current onboarding state, perform only its single returned next_action, then read state again; do not batch or infer later stages. Continue until status is verified, installation_complete is true, and next_action is null. Keep all secrets, source snapshots, proof markers, and scratch files outside the repository.

+ Full access
5.5 Extra High
  1. 1. Open your repo in Codex, Claude Code, Cursor etc.
  2. 2. Paste the install prompt.
  3. 3. Your agent reads the install doc and shows you an implementation plan for approval.