The metric definitions template (metrics.md)
Six metrics, each with a formula, a single source of truth, an owner, a tolerance for disagreement, and a refresh cadence. Copy it, argue about the source-of-truth column with the three people who own the numbers, save it as metrics.md, and the agent computes every tile from it on Monday.
# Revenue metrics: how we count
Last changed: {{DATE}} by {{OWNER}}. Every change is a commit with a reason.
## Pipeline created
- Formula: sum of CRM amount for deals that entered stage Qualified during the period, counted at qualification date
- Source of truth: {{CRM}} stage history
- Also reported by: attribution tool (marketing-sourced opportunities); label it that, do not blend
- Owner: {{CRO}} Tolerance: 10% Refresh: weekly
## Pipeline coverage
- Formula: open Qualified+ pipeline with close date in the current quarter ÷ (quarter quota − closed-won to date)
- Source of truth: {{CRM}}; quota from {{FINANCE_SHEET}}
- Owner: {{CRO}} Tolerance: n/a (derived) Refresh: weekly
## CAC by channel
- Formula: paid spend for the channel in the period ÷ new customers whose first touch (CRM original source) was that channel; blended = total paid spend ÷ all new customers
- Source of truth: spend from ad platform exports; customer and first touch from {{CRM}}; 'new customer' = first paid invoice in {{BILLING}} during the period
- Owner: {{CMO}} Tolerance: 15% Refresh: weekly, read monthly
## Win rate
- Formula: closed-won ÷ (closed-won + closed-lost), by close date, count of deals; also report by amount
- Source of truth: {{CRM}}
- Owner: {{CRO}} Tolerance: 5 points Refresh: weekly, trailing 90 days
## Net revenue retention (12-month)
- Formula: current MRR from customers who had an active subscription 12 months ago ÷ their MRR 12 months ago
- Source of truth: {{BILLING}} subscription history
- Also reported by: {{CRM}} renewal amounts; label as CRM view
- Owner: {{FINANCE}} Tolerance: 3 points Refresh: monthly
## MRR
- Formula: sum of active subscription amounts normalized to monthly, at period end
- Source of truth: {{BILLING}}
- Also reported by: {{CRM}} closed-won ARR ÷ 12; print the delta
- Owner: {{FINANCE}} Tolerance: 3% Refresh: weekly
## Joins
- Billing customer -> CRM company: match on domain; exception if match rate < 90%
- New customer -> first-touch channel: CRM original source; exception if < 90%
## Rules
- One source of truth per metric. Other tools' numbers are deltas, printed, never averaged.
- The agent reports exceptions; an owner resolves them by editing this file or fixing the source.
- A definition change recomputes the trailing 13 periods on the next run and is noted on the page.
The tolerance column is the one most teams skip and the one that makes the Monday diff short. Without it every 2% timing difference is an exception; with it the list is the two things somebody actually has to rule on.
The stack
- Per week
- one run over a few thousand CRM records, a year of billing, and 26 spend files; a few dollars of tokens on a mid-tier model, less with the definitions cached
- CRM + billing + Slack
- free over MCP; ad spend as CSV exports until the API is worth the hour
- The dashboard
- one static file; no BI license, no warehouse required to start
- Setup
- two days: ninety minutes on definitions with three leaders, a day on connections and joins, three Mondays by hand
- The saving
- the twenty reconciliation minutes at the top of every leadership meeting, and the BI project that never got trusted
The problem
The word 'pipeline' means four things at most companies, and the revops dashboard is where the four collide. The CRM counts deals created this quarter in any stage. The marketing attribution tool counts opportunities it can tie to a campaign, which excludes half of outbound. Finance counts deals with a close date in the quarter and an amount above a threshold. The CEO's slide uses whichever number was highest on the day the deck was built. Nobody is lying. Each tool answers a slightly different question, and no document anywhere says which question the company's number is supposed to answer.
So the Monday leadership meeting spends its first twenty minutes reconciling. Marketing says CAC fell. Sales says pipeline coverage is 2.1x. Finance says bookings are behind. All three pulled from tools that do not share a definition of a customer, a channel, or a quarter, and the person who could reconcile them, the RevOps lead, is doing it live in a spreadsheet with the room watching. The meeting that was supposed to be about decisions is about arithmetic, and the decision gets deferred to a follow-up that becomes the same meeting a week later.
The standard fix is a BI tool: a license, a warehouse, a three-month implementation, and a dashboard with forty tiles that reproduces the disagreement in prettier colors, because the definitions were never written down before the tiles were built. Six months in, the dashboard shows one number for pipeline and the CRO still keeps a spreadsheet, because the tile's formula is buried in a modeling layer nobody on the revenue team can read. Trust in a number comes from being able to see how it was made, and BI tools are built to hide exactly that.
The thing that was always missing is not a tool. It is a page that says, for each of six metrics, what the formula is, which system is the source of truth, who owns the definition, and how far the other systems disagree this week. Once that exists, the dashboard is almost a by-product. A Claude agent can read every source over MCP, compute each metric as written, print the deltas, and write the page. The reconciliation is the work; the dashboard is the receipt.
How it works
- 01 Fire Monday 6am Claude Code (scheduled)period ending yesterday
- 02 Read definitions metrics.mdformula, source, owner, tolerance
- 03 Read the CRM HubSpot / Salesforce (MCP)deals, stages, sources, dates
- 04 Read spend + billing ads exports, Stripe (MCP)spend by channel; customers, invoices
- 05 Compute Claudesix metrics, exactly as defined
- 06 Reconcile Claudeevery other tool's number, the delta
- 07 Write the page Claudeone static HTML file + JSON
- 08 Post the diff Slack (MCP)movement, exceptions, owners
- You write metrics.md: six to eight metrics, each with a formula, a single source of truth, an owner, and a refresh cadence, and the agent is forbidden to compute a metric any other way
- Every Monday at 6am a scheduled Claude Code job reads the CRM over MCP, the ad spend exports or APIs, product usage, and billing (Stripe over MCP, or a warehouse table)
- It computes each metric from its definition, and separately records what every other tool claims for the same concept, so the delta is a number on the page, not a rumor in the meeting
- It writes one static HTML page: the six numbers, the trend, the reconciliation table, and a definitions section that quotes metrics.md, so the formula is one scroll from the tile
- It posts a diff to Slack: what moved, what disagrees by more than the tolerance, and which definition an owner needs to rule on this week
- When a delta exceeds tolerance for two weeks, the owner edits metrics.md or fixes the source; the agent never resolves a disagreement by picking a number
See it run
The playbook
Write the metric definitions before you touch a revops dashboard tile
Sit the CRO, the CMO, and finance in one room for ninety minutes and leave with six definitions written in sentences. Pipeline created: deals that reached the qualified stage this period, counted at qualification date, amount from the CRM. Pipeline coverage: open qualified pipeline with a close date in the quarter, divided by remaining quota. CAC by channel: paid spend in the period divided by new customers whose first touch was that channel, first touch from the CRM's original source field. Win rate: closed-won divided by closed-won plus closed-lost, by close date. NRR: this month's recurring revenue from customers who were paying twelve months ago, divided by what they paid then, from billing. Each one names a formula, a source of truth, and an owner.
The definitions template below is the shape. The point of the meeting is not the formulas, which are standard; it is the source-of-truth column. When marketing's attribution tool and the CRM disagree on how many customers a channel produced, one of them is the number and the other is a delta to explain. Deciding that once, in writing, with names attached, is the entire reason the rest of this play works.
Save it as metrics.md in the repo. It is the file the agent reads first on every run, and it is the file the CRO reads when a tile looks wrong. A definition change is a commit, with a date and an author, so when Q3's pipeline number differs from Q2's you can see whether the business changed or the formula did.
TipOne source of truth per metric, even when a second tool is closer to right on some days. A dashboard that averages two sources has no owner and will not be trusted by either.
Connect every source read-only over MCP, and check that the joins exist
Connect HubSpot or Salesforce and confirm the agent can list deals with stage history, amounts, original source, and dates. Connect billing: Stripe has an official MCP server (listed in the MCP directory) that exposes customers, subscriptions, and invoices; if revenue lives in a warehouse, Snowflake's server or a Postgres connection does the same job. Ad spend is the least glamorous part: most teams export LinkedIn and Google Ads spend by campaign and day into a folder the agent reads, and move to the APIs later if the export ritual breaks.
Then check the joins before you compute anything. CAC by channel needs a customer in billing to match a company in the CRM, which needs a shared key, usually the domain. NRR needs the customers who were paying twelve months ago, which needs billing history, not a current snapshot. Run the agent once with the instruction 'list every join you could not make and what percentage of records it affects' and fix the worst gap first. A dashboard built on a 60% join rate is a dashboard that will be wrong in a way nobody can see.
The agent reads everything and writes only files and a Slack message. It does not update a CRM field to make a join work, does not tag deals with a channel it inferred, and does not create records in billing. The hygiene agent proposes those fixes for a human to approve; this one reports what it found and computes with what is there.
- Reads: CRM deals with stage history, amount, original source, create/qualify/close dates, associated company domain
- Reads: paid spend by channel, campaign, and day; new-customer first-touch source from the CRM
- Reads: billing customers, subscriptions, invoices, and cancellations with dates; product usage per account if you have it
- Reads: metrics.md and last week's computed table, so the page shows movement and the diff has a baseline
- Writes: dashboard/index.html, data/YYYY-MM-DD.json, and one Slack post. Never a field in any source system
Write the brief so every number arrives with its formula and its deltas
The brief has two rules that make the page trustworthy. First: compute each metric exactly as metrics.md defines it, from the named source of truth, and print the formula and the row count beside the number. A tile that says 'Pipeline created $2.4M' is a claim; a tile that says '$2.4M, 61 deals reached Qualified between Sep 1 and Sep 7, HubSpot' is a fact someone can check in two minutes. Second: for every metric where another tool reports the same concept, record that tool's number and the delta, and never blend them. The reconciliation table is the part of the page the leadership team will actually read.
Give it a tolerance per metric and a rule for what to do when the delta exceeds it. A 3% gap between the CRM's pipeline and the attribution tool's is noise from timing; a 30% gap for two consecutive weeks is a definition problem or a broken sync, and the brief tells the agent to name the metric, the two numbers, the likely cause from what it can see (records missing a source, deals with no close date, customers with no matching domain), and the owner from metrics.md. It does not choose a winner. Resolving a disagreement is the owner's job; making it visible on Monday morning is the agent's.
Tell it what the page is not. No forecast, no commentary on whether the numbers are good, no recommendations. The forecast brief and the pipeline digest do the narrative work. This page is the number set they both cite, and the moment it starts editorializing, it becomes one more opinion in the meeting instead of the thing the opinions are about.
Every Monday at 6:00am {{TIMEZONE}}, rebuild the revenue metrics page for the period ending yesterday.
Read metrics.md first. It defines each metric's formula, source of truth, owner, tolerance, and refresh cadence. Compute every metric exactly as written, from the named source only. If a source or a required field is unavailable, report the metric as UNAVAILABLE with the reason; do not substitute another source.
Read from {{CRM}}: deals with stage history, amount, original source, company domain, and create, qualify, and close dates for the trailing 13 months.
Read from {{ADS_FOLDER or ADS_API}}: spend by channel, campaign, and day for the trailing 13 months.
Read from {{BILLING}}: customers, subscriptions, invoices, and cancellations with dates for the trailing 13 months; match customers to CRM companies on domain.
For each metric, produce: the value for the period, the same value for the prior period and the same period last year where the definition allows, the formula quoted from metrics.md, the record count behind it, and the source.
Reconciliation: for each metric, also record what every other connected tool reports for the same concept (for example: attribution-tool opportunities vs CRM qualified deals; billing MRR vs CRM closed-won ARR / 12). Print the delta in absolute and percentage terms. Do not average, blend, or pick between sources. If a delta exceeds the metric's tolerance, add a row to the exceptions list with: metric, the two numbers, the most likely cause visible in the data (unmatched domains, missing source field, deals with no close date, timing), and the owner from metrics.md.
Joins: report the match rate for every join (billing customer to CRM company, new customer to first-touch channel). A join below 90% is an exception.
Write:
1. data/{{DATE}}.json with every computed value, count, and delta.
2. dashboard/index.html: one page. Six metric tiles with value, period change, formula, count, and source. A trend table for the trailing 13 periods. The reconciliation table. The exceptions list. A definitions section that reproduces metrics.md verbatim. Ink on paper, no charts library, no external requests.
3. A Slack post to {{REVOPS_CHANNEL}}: each metric with its change, the exceptions with owners, and the join rates. No commentary on whether the numbers are good.
Write nothing to any source system.
TipPut the row count on every tile. 'Pipeline created $2.4M (61 deals)' gets checked; '$2.4M' gets argued with.
Build the page as one static file, not a BI project
The dashboard is a single HTML file the agent writes every Monday: six tiles, a trend table, the reconciliation table, the exceptions, and the definitions, in that order. No chart library, no login, no external requests, so it opens from a shared drive or an internal URL in under a second and prints to one page for the board pack. The agent builds it from a template you review once; the numbers change weekly, the layout does not. If you want a chart, a table of thirteen periods with a sparkline drawn in inline SVG is enough, and it is the agent's job to draw it, not yours.
Resist the pull toward more tiles. Six metrics that everyone agrees on and can trace to a formula are worth more than forty that reproduce every tool's own dashboard. When someone asks for a seventh metric, the answer is a definition in metrics.md with an owner, which is a harder ask than 'add a tile', and that friction is a feature. Every metric on the page should have someone who will defend its formula in the meeting.
If the team already lives in Google Sheets, write the same table to a Sheet instead of an HTML page and let the page be the printable version. What matters is that the values come from one run against one definitions file, so the Sheet, the page, the Slack post, and the board slide all say the same number for the same week. The funnel pillar is the map of which of these metrics belongs to which stage.
Run the diff on Monday and work the exceptions, not the tiles
Schedule the run for 6am Monday as a scheduled Claude Code job so the page and the Slack diff are waiting before the leadership meeting. The diff is short on purpose: six metrics with their movement, the exceptions with owners, and the join rates. If the diff has no exceptions, the meeting starts with the decisions. If it has two, the two owners come to the meeting knowing which definition or which sync they need to rule on, and the arithmetic conversation is over before it starts.
Run the first three Mondays by hand with the RevOps lead and read every exception against what they know. The first run will find things: the 40 deals with no original source that make CAC by channel undercount every channel, the billing customers whose domain is a personal email, the attribution tool counting re-opened opportunities as new. Each is either a data fix (route it to the hygiene agent), a definition fix (a commit to metrics.md), or a known gap you write into the definitions section so nobody rediscovers it in November.
Watch the exceptions list shrink week over week. That shrinking line is the real output of the play. A reconciliation table with zero exceptions for a month means the company has one definition of its numbers and every tool is being held to it, which is what 'single source of truth' was supposed to mean before it became a vendor slogan.
TipNever let the agent close an exception. It reports; an owner rules by editing metrics.md or fixing the source. A dashboard that quietly resolves disagreements is a dashboard that hides them.
Review the definitions quarterly, and version every change
Once a quarter, read metrics.md with the same three people who wrote it. Definitions drift because the business does: a new self-serve tier means 'customer' needs a floor, a partner channel means CAC needs a new bucket, a move to annual billing changes what NRR looks like month to month. Change the file, commit it with a note, and have the agent recompute the trailing thirteen periods under the new definition on the next run, so the trend line is consistent and the page says when and why the formula changed.
Keep the old computations. The data folder holds a JSON per week, so 'what did we report to the board in March' is a file, not a memory, and 'what would March have looked like under the new definition' is one instruction to the agent. That history is also how you catch a broken sync: a metric that steps 20% in one week with no business explanation is a source problem, and the weekly JSONs show the exact week it started.
Publish the definitions inside the company. A one-page 'how we count' document, generated from metrics.md, kills more Slack threads than any dashboard. When a new sales leader asks why their pipeline number differs from the page, the answer is a link to the formula and the source, and the conversation moves to whether the definition should change, which is the right conversation to be having.
Inside the prompt
The scoring prompt is short, but every line is there for a reason. Here is what each one is doing and why.
- Read metrics.md first"Compute every metric exactly as written, from the named source only"
- The definitions file is the product. The agent is a calculator that refuses to improvise.
- UNAVAILABLE, not substituted"do not substitute another source"
- A metric quietly computed from a fallback source is how two weeks' numbers stop being comparable.
- Formula and count on every tile"the formula quoted from metrics.md, the record count behind it, and the source"
- Trust comes from being able to see how the number was made. BI tools are built to hide that.
- Never blend"Do not average, blend, or pick between sources"
- The reconciliation table exists to show disagreement. Blending erases the information the meeting needs.
- Exceptions carry an owner"the most likely cause visible in the data ... and the owner from metrics.md"
- An exception with a name attached gets ruled on Monday. One without a name is a Slack thread.
- Join rates as exceptions"A join below 90% is an exception"
- CAC and NRR are only as real as the domain match. The rate on the page every week is how you notice a broken sync in week one.
- No commentary"No commentary on whether the numbers are good"
- The page is what the digest and the forecast brief cite. The moment it editorializes it becomes an opinion.
What you get
The Monday diff as it lands in the RevOps channel. Six numbers with their movement, then the two things somebody has to rule on, each with an owner from the definitions file. No commentary.
REVENUE METRICS · week ending Sep 7 · dashboard/index.html
Pipeline created $2.41M (61 deals) +9% wow HubSpot, qualified date
Pipeline coverage 2.3x was 2.1x open qualified w/ Q3 close ÷ remaining quota
CAC (blended) $4,120 -6% $187k paid spend ÷ 45 new customers
Win rate 24% (28 of 117) = closed-won ÷ closed, by close date
NRR (12-mo) 108% +1pt Stripe, cohort paying Sep 2025
MRR $612k +$14k Stripe, active subscriptions
EXCEPTIONS (2)
1. Pipeline created: CRM says 61 qualified deals; attribution tool says 42 opportunities (-31%, tolerance 10%). 17 of the missing 19 are outbound deals with no campaign touch; the tool cannot see them by design. Owner: CMO. Options: accept the CRM number as the definition (already the source of truth) and label the attribution number 'marketing-sourced', or add outbound as a channel in the tool.
2. New customer -> first-touch join at 88.1% (tolerance 90%). 5 of 45 new customers have no original source in HubSpot; CAC by channel undercounts every channel by ~11%. Owner: RevOps. Likely fix: the 5 came in via the partner form, which does not set original source.
JOINS billing->CRM 94.2% (24 unmatched, 19 are personal-email domains) · new customer->first touch 88.1%
Definitions unchanged since 2026-07-14 (commit 3f9a2c1).
- definitiondeals that reached Qualified in the period, counted at qualification date, CRM amount
- Quoted from metrics.md on the tile. The formula is one scroll from the number, not buried in a modeling layer.
- source of truthHubSpot, stage history
- One system. The attribution tool's opportunity count is a delta to explain, not a second answer.
- count61 deals, $2.41M
- The row count makes the number checkable in two minutes. A tile without a count is a claim.
- deltaattribution tool: 42 opportunities (-31%, tolerance 10%)
- Printed, not blended. 17 of 19 missing are outbound deals with no campaign touch; the tool cannot see them by design.
- ownerCMO
- From metrics.md. The exception names who rules on it; the agent never picks a winner.
- historydata/2026-09-08.json; definition unchanged since 2026-07-14
- Every week is a file. 'What did we tell the board in March' is a lookup, and a definition change is a dated commit.
Pitfalls to avoid
Tiles before definitionsEvery BI project that reproduced the argument in color started with the tiles. Write metrics.md first; the page is the by-product.
Blending sourcesA metric averaged from two systems has no owner and will be trusted by neither. One source of truth per metric; every other tool's number is a delta on the page.
Letting the agent resolve a disagreementIt reports the two numbers, the likely cause, and the owner. An owner rules by editing the definition or fixing the source. Silent resolution is how dashboards hide broken syncs.
Skipping the join checkCAC and NRR are only as good as the domain match between billing and the CRM. Print the match rate every week and treat anything under 90% as an exception.
Forty tilesSix metrics everyone can trace beat forty that mirror each tool's own dashboard. A new tile costs a definition with an owner, and that friction is the point.
Adding commentaryThe moment the page says whether the numbers are good, it is one more opinion in the meeting. The digest and the forecast brief do the narrative; this page is what they cite.
Questions people ask
- What is a revops dashboard?
- One page that shows the revenue metrics the whole company runs on, pipeline created, pipeline coverage, CAC by channel, win rate, net revenue retention, and recurring revenue, computed from one written definition per metric and one named source of truth each. The version on this page is built weekly by a Claude agent that reads the CRM, ad platforms, product usage, and billing over MCP, and it adds the part most dashboards leave out: a reconciliation table showing how far every other tool disagrees with the defined number, and who owns the definition.
- Why not just use a BI tool?
- You can, later. The reason BI dashboards get built and then ignored is that the definitions were never written before the tiles were, so the dashboard reproduces the disagreement between tools in a modeling layer the revenue team cannot read. Write metrics.md first, run this play for a quarter, and if you then want a BI tool you will hand the implementer a definitions file and a year of reconciled weekly JSON, which is the part every BI project is missing on day one.
- What makes it a single source of truth?
- Not the page. The definitions file: one formula, one source system, and one owner per metric, versioned in a repo. The page is a single source of truth because every number on it can be traced to that file and the disagreements with other tools are printed rather than hidden. A dashboard that averages sources or picks the friendlier number is a single source of something, but not truth.
- Which sources does it need?
- The CRM for pipeline and win rate, billing for MRR and NRR, and paid spend by channel for CAC. Product usage is optional and useful for a seventh metric later. HubSpot, Salesforce, and Stripe have official MCP servers; ad spend usually starts as CSV exports. The MCP directory on this site lists what each server exposes.
- What if two tools disagree and both are defensible?
- Then the definition is missing a sentence. The attribution tool that counts only campaign-touched opportunities is answering 'how much pipeline did marketing source', and the CRM is answering 'how much pipeline did we create'. Both are useful; only one is the company's pipeline number, and metrics.md says which. The other becomes its own metric with its own definition, or a labeled delta on the page.
- Does the agent write to any of the source systems?
- No. It reads the CRM, billing, and spend, and writes a JSON file, an HTML page, and a Slack post. When it finds a data problem, such as deals with no original source, it lists it as an exception with an owner. The CRM data cleanup agent on this site is the play that proposes the fixes for a human to approve.
- How long does it take to build?
- Two days plus three Mondays. Ninety minutes with the CRO, CMO, and finance writing six definitions; a day connecting sources over MCP and checking join rates; then three weekly runs read by hand with the RevOps lead before the diff posts on its own.
Related plays
- Pipeline Review on Autopilot: A Monday Digest from a Claude Routine →The narrative that cites this page's numbers: what moved, what went stale, what has no next step.
- Sales Forecasting with AI: A Weekly Claude Agent That Builds the Bottoms-Up Forecast Brief →The forecast call brief; coverage on this page is the number it argues against.
- CRM Data Cleanup on a Schedule: A Claude Agent That Proposes, You Approve →Where the exceptions this page finds (missing sources, unmatched domains) get fixed, with approval.
- Claude across the GTM funnel →Which of these six metrics belongs to which stage, and the plays that move each one.
- Claude + HubSpot integration guide →Reading deals, stage history, and original source over MCP, read-only.
- MCP servers for GTM →The official Stripe and Snowflake servers this play reads billing and warehouse data through.
- Claude for RevOps →The rest of the RevOps playbook this dashboard sits on top of.