FFastero

Connect any database. Ask in plain English.

Try free
Back to blog

Blog article

How to Build Sales Reports Your CRM Can't Give You

Salesforce Reports and HubSpot dashboards are great until you need to join a deal to a Stripe invoice, a support ticket, or a product login. Here's what CRM reporting can't do, why it hits a wall, and how to build the reports it was never designed for.

Fastero Dev TeamFastero Dev Team
2026-07-18
sales reportingSalesforceHubSpotRevOpsCRM analyticsreporting
How to Build Sales Reports Your CRM Can't Give You

Every RevOps person hits the same wall eventually. You've built the pipeline report, the forecast rollup, the rep leaderboard — all inside Salesforce or HubSpot, all clean, all fine. Then someone in a leadership meeting asks a question like "which closed-won deals never actually started paying us" or "does deal size correlate with how much a customer uses the product," and you realize the answer doesn't live in your CRM. It lives in the gap between your CRM and two or three other systems, and your CRM's report builder has no idea those systems exist.

This isn't a knock on Salesforce or HubSpot. Their reporting is genuinely good at what it's built for: rollups, filters, and views over objects that live inside the CRM's own schema. The problem is structural — the report builder can only report on what's in the CRM, joined the way the CRM's object model allows. The moment you need billing data, product usage data, or support data in the same report, or a join shape the platform doesn't support, you're out of options inside the tool.

The 5 reports your CRM can't build

1. Closed-won revenue vs. actual collected revenue. Salesforce and HubSpot both report on the deal amount field — the number a rep typed in, or that rolled up from a quote. Neither knows what Stripe or your billing system actually collected. If a deal closes at $40k but the customer negotiates a discount at contract signing that never gets reflected in the CRM record, or a mid-contract downgrade happens six months later, your CRM's revenue report is now wrong and has no way of knowing it. You need Stripe or Chargebee data joined against the CRM to see forecasted revenue against collected revenue, and neither CRM has a native connector that does this at the report level.

2. Deal outcomes correlated with product usage. "Do bigger deals churn faster?" "Do deals sourced from paid ads activate at a lower rate than deals sourced from referrals?" These are product-usage-times-deal-attribute questions, and your CRM has no visibility into product usage at all unless someone built a sync that writes usage events back as CRM activity records — which is its own maintenance burden and usually only captures a handful of predefined events, not the raw event stream you'd actually want to slice.

3. Cross-system funnel: lead to paid to activated to expanded. Your CRM tracks lead-to-close. Your product tracks signup-to-activation. Your billing system tracks activation-to-expansion-to-churn. Each of those handoffs is a different data owner, and a single report that walks the whole funnel — from marketing-qualified lead through to net revenue retention — requires stitching three systems by a shared identity key (email, account ID) that none of them agree on by default.

4. Support burden by deal size or CS assignment. If you want to know whether your highest-ACV accounts are also generating the most support tickets — a genuinely useful signal for both CS staffing and renewal risk — you need Zendesk or Intercom ticket volume joined against CRM account records. Neither CRM ships a native support-desk join, and building a custom object to mirror ticket data means keeping a sync running forever.

5. Custom attribution that doesn't fit the CRM's model. Salesforce's campaign influence model and HubSpot's default attribution reports both assume a fairly linear touch model. The moment your GTM motion is more complicated — self-serve signups that get sales-assisted later, or multi-threaded enterprise deals where the "first touch" contact isn't the one who ultimately signs — you're fighting the attribution model instead of using it. Building your own weighting scheme requires raw touch data joined against deal outcomes, which is a SQL problem, not a report-builder problem.

Why CRM reporting hits a wall

None of the above is a bug. It's what happens when you push a schema-bound report builder past the boundary it was designed for. Three concrete limitations show up over and over:

Report types are fixed at creation time in Salesforce. A classic Salesforce report runs against a report type, and a report type defines which objects can be joined and how. Standard report types support a small number of object relationships; anything outside that (a custom object joined to two other custom objects, say) needs a custom report type built by an admin ahead of time, and even then you're capped — cross-filters are limited to a handful per report, and joined reports (which let you combine unrelated report types on one page) cap out at five blocks. You cannot ad-hoc join two arbitrary objects the way you'd write a SQL join.

HubSpot's custom report builder has similar ceilings. It supports cross-object reporting for a defined set of object pairs (deals with contacts, deals with companies, tickets with contacts, and so on), but reporting across three or more objects, or objects HubSpot doesn't consider "related" out of the box, isn't supported without custom-coded workarounds or a third-party BI layer sitting on top of the HubSpot API.

Neither system was built to ingest external data as a first-class citizen. You can sync external data into a CRM as custom objects or properties, but that's a one-way street that needs building and maintaining, and it turns your CRM into a partial data warehouse instead of a system of record. Every one of the five reports above needs data your CRM doesn't have and, more importantly, was never meant to have — the object model doesn't have a "Stripe invoice" or "product session" concept, and forcing one in usually means degrading either the CRM's performance or its clean data model.

There's also a quieter limitation worth naming: export row limits. Salesforce reports cap exports well below what you need for a full account-level analysis on any business past a few thousand accounts, and HubSpot's export limits on custom reports mean anything approaching real analysis eventually forces you into "export, stitch in Excel, repeat" — which is where most RevOps teams actually end up, whether or not they call it a workaround.

Fastero

Connect your database. Ask questions. Get dashboards.

Postgres, BigQuery, Snowflake, and 10+ sources — live-connected, AI-powered, no dashboard builder learning curve.

Try free →

Three approaches to getting past the wall

Export to spreadsheet. This is where almost everyone starts, and for a one-off question it's fine. Export deals from Salesforce, export charges from Stripe, VLOOKUP or Power Query them together on email or account ID. The problem isn't that it doesn't work once — it's that it doesn't survive contact with "can you refresh this for the board meeting next month." Every refresh is a fresh export, a fresh manual join, and a fresh chance for someone to fat-finger a VLOOKUP range. It also doesn't scale past a few thousand rows before Excel itself starts fighting you.

A BI tool on top of a warehouse. Land Salesforce/HubSpot, Stripe, and product data in Snowflake or BigQuery via Fivetran or similar, then build the join logic in Looker, Tableau, or Metabase. This is the "correct" enterprise answer and it genuinely works — but it's a real project. You're now paying for a warehouse, an ELT tool, and a BI license, and you need someone who can write and maintain the transformation layer. For a RevOps team of one or two people at a company under a few hundred employees, this is often more infrastructure than the problem justifies, and it's slow to stand up when the leadership question is "we need this by Friday."

Connect the sources directly and query across them. This is the middle path — and it's the approach tools like Fastero take: connect Salesforce or HubSpot, Stripe, and your product database directly, without standing up a warehouse or an ELT pipeline first, and write the join as a query or ask for it in plain language. You get the cross-system join capability of the BI-tool approach without the infrastructure commitment, at the cost of not owning a permanent warehouse of your own — a real tradeoff if you expect to need heavier, longer-horizon analytics later, but usually the right one for the specific "answer this in the next few reports" problem this post is about.

Step-by-step: closed-won deals that never activated

Here's the report from the top of this post, worked through end to end. The business question: which deals closed as won but the account never did anything in the product afterward — a strong signal of a broken handoff between sales and onboarding.

1. Identify your join key. Almost never the CRM's internal record ID. Usually the account's primary contact email, or a shared account/company domain if you're joining at the account level rather than the individual level. Confirm the join key actually matches cleanly before building anything on top of it — this is the step people skip and regret.

2. Pull closed-won deals with a close date old enough that "never activated" is a meaningful signal, not just "hasn't gotten around to it yet." 30 days is a reasonable default; adjust to your onboarding SLA.

3. Pull product activation events for the same accounts. Whatever your product defines as first meaningful use — first login, first workflow run, first report generated.

4. Left-join deals to activation events and keep only the deals with no match.

-- Closed-won deals with no product activation event within 30 days of close
SELECT
  deals.deal_name,
  deals.amount,
  deals.close_date,
  deals.owner_name,
  deals.account_email
FROM crm_deals AS deals
LEFT JOIN product_events AS activation
  ON deals.account_email = activation.user_email
  AND activation.event_type = 'activation'
  AND activation.event_time BETWEEN deals.close_date AND deals.close_date + INTERVAL '30 days'
WHERE deals.stage = 'closed-won'
  AND deals.close_date < NOW() - INTERVAL '30 days'
  AND activation.user_email IS NULL
ORDER BY deals.close_date DESC;

5. Route the output, don't just look at it. A static report that someone has to remember to check is barely better than not having it. Send the list to whoever owns onboarding — CS or the AE, depending on your handoff model — so each row becomes a follow-up, not a line in a spreadsheet nobody re-opens.

Setting up alerts for pipeline anomalies

Reports answer questions you already thought to ask. Alerts catch the things you didn't. The pattern worth building once you have cross-system data flowing is a compound condition: not "deal size dropped" alone (too noisy, happens constantly during normal negotiation) and not "stage regressed" alone (also common, not always bad), but both together — a deal that moved backward in stage and the amount dropped in the same window is a much stronger signal that a deal is actually in trouble, not just going through normal back-and-forth.

-- Deals that regressed in stage AND dropped in amount in the last 7 days
SELECT
  current.deal_name,
  previous.stage AS prior_stage,
  current.stage AS current_stage,
  previous.amount AS prior_amount,
  current.amount AS current_amount,
  current.owner_name
FROM crm_deals AS current
JOIN crm_deal_history AS previous
  ON current.deal_id = previous.deal_id
  AND previous.snapshot_date = CURRENT_DATE - INTERVAL '7 days'
WHERE current.stage_order < previous.stage_order   -- moved backward
  AND current.amount < previous.amount * 0.9        -- dropped more than 10%
  AND current.stage NOT IN ('closed-won', 'closed-lost');

Getting the "previous state" snapshot is the part that trips people up — neither Salesforce nor HubSpot gives you a clean daily snapshot of deal state out of the box, which is why this kind of alert usually needs either field history tracking (Salesforce) or a small scheduled job that snapshots deal state daily somewhere queryable. Once that's in place, the alert itself is a scheduled query with a Slack or email destination instead of a dashboard someone has to remember to check.

The honest caveat

If you're on Salesforce Enterprise or Unlimited edition with CRM Analytics (formerly Tableau CRM, formerly Einstein Analytics) licensed, you can do a meaningful chunk of what's described here natively — external data connectors, cross-object dataflows, and dashboards that go past what standard reports allow. It's real capability. It's also roughly $150/user/month on top of your existing Salesforce licensing, plus a genuinely nontrivial learning curve on dataflows and SAQL that most RevOps teams don't have headcount to absorb. If you already have it, use it — you're paying for exactly this problem. If you don't, standing it up from zero is usually a slower and more expensive path to the same five reports than connecting your systems directly and writing the joins yourself.

The bottom line

CRM reporting is good at reporting on the CRM. The moment the question spans billing, product usage, or support data — or needs a join shape the report builder wasn't built for — you're not going to find the answer by building a fancier Salesforce report. You need the data connected outside the CRM's object model, a join key that actually resolves cleanly across systems, and a way to route the output as an alert instead of a dashboard nobody opens.

See how Fastero handles Salesforce pipeline monitoring, revenue ops monitoring, and customer 360 reporting — or compare the option space in our breakdown of the best revenue intelligence platforms.


Want the "closed but never activated" report running against your own CRM and product data? Start a free trial—connect Salesforce or HubSpot alongside your product database, and get the join without building a pipeline first.

Related: Revenue monitoring | Salesforce pipeline monitoring | Best revenue intelligence platforms

Ready to try it yourself?

Connect your database, ask questions in plain English, and get live dashboards — in under 2 minutes. No credit card required.