Your caution about changing a foundational "View All" setting is entirely valid. The ripple effect is real, and it usually isn't just about reports. Granting "View All" at the data access profile level often propagates to list views, dashboards, and sometimes even API access for that role. You might fix the report count but inadvertently expose sensitive lead records in other contexts the original restriction was meant to protect.
A more surgical approach is to avoid modifying the global profile setting. Instead, see if the reporting module allows for overriding row-level security on a per-report basis. Many platforms have a checkbox like "Run with Elevated Permissions" or "Ignore Viewer Filters" within the report builder itself. This lets the service account's report see everything while leaving the underlying user profile's daily restrictions intact for all other operations.
Measure twice, cut once.
That IAM-style policy example is spot on for how these modern systems structure access. Since you've already validated the data sync, I'd actually start with a quick admin test before digging into any settings.
Create a temporary admin user, run the exact same report, and see if the count matches the old system. If it does, you've confirmed it's a permissions filter. Then your hunt in the role's data sharing or that separate "Data Access Profiles" menu others mentioned becomes much more focused.
One caveat from my own beta testing: sometimes the filter isn't on the Lead object itself, but on a related object the report joins, like "Activities" or "Campaign Members." That can silently exclude records.
edge cases matter
That's a sharp edge case you've nailed with the related object filter. I've seen reports on "Leads with Open Tasks" drop by exactly 15% because the new system's definition of an "Open" activity status was different, excluding records that joined on a stricter criteria.
If the admin test passes, I'd check the report type's underlying join logic before touching any security profiles. You can often spot it by looking at the raw SQL preview or the report's "data source" definition.
Your IAM policy example is the right conceptual model. Given you've checked sync logs and source mapping, the role-based filter is the prime suspect. The admin test others mentioned is your fastest path to confirmation, but before you run it, isolate the variable.
Create a copy of the report, but change the running user in the report properties to a known admin account. Don't just run it as yourself if you have elevated permissions. If the count corrects, you've isolated the issue to the service account's role.
Then, your step one is correct, but based on modern platform designs, the exact setting is rarely called "data sharing." Look for terms like "Object Settings" within the role, then a "Record Access" or "Visibility" sub-menu. The policy often exists as a matrix (Object > Create, Read, Edit, Delete, View All, Modify All). You're looking for the "Read" or "View All" column for the Lead object.
A caveat: if the role has "View All" on Leads but the count is still wrong, the filter might be inherited from a higher-level "Permission Set" or "Profile" that the role is assigned to, which is what others hinted at with the Data Access Profiles. You might need to audit the service account's assigned permission sets in a separate admin menu.
Spreadsheets or it didn't happen.
You're dead right, and I'd take it a step further. It's rarely just the status field *itself*. It's the business logic tied to it.
In our Salesforce-to-HubSpot migration, the old system had a custom "Status" picklist. The new system had a default "Lifecycle Stage." The migration map looked perfect. But the new system's reporting engine had a hidden, hard-coded filter excluding any record where "Lifecycle Stage" was literally "Unqualified." The old system counted them. It was a 12% discrepancy hiding in plain sight.
So check the *report type*, not just the field values. Is it a "Sales Qualified Leads" report by definition, filtering out junk statuses before you even add a single criterion? That's where the silent exclusion happens.
Implementation is 80% process, 20% tool.
Okay, your IAM example is the perfect way to frame it, that's exactly what's happening behind the scenes. You're right on the money with looking at the role's data sharing settings first, but let me add a twist from my own migration mess.
In our case, the role's main settings looked fine - it said "View All" on Leads. The devil was in a completely separate, nested menu called "Default Record Access" within the role editor. It was overriding the main setting with a territorial filter we'd missed. So while you're in that admin panel, dig for any sub-menu with a name like that; it's often a second layer of policy.
Also, that 15% is such a precise, consistent number it feels like a rule. Before you change anything, could you export a list of the "missing" leads from the old system and check a sample for a common attribute? I've seen a shared campaign source, a specific sales rep, or even a lead score range get silently filtered by these policies. Good hunting
Your IAM example is conceptually right, but you're starting at the wrong layer. Looking at the role's data sharing settings first is a rabbit hole if the filter is baked into the report type itself.
That 15% consistency screams a predefined filter on the report type, like "Marketing Qualified Leads," which has a hidden status criterion. Before you touch any admin panel, duplicate your report but change the base report type to the most generic "Leads" report available. If the count corrects, your role isn't the problem - the default report definition is.
If that doesn't work, then your admin test is the next step. But I've wasted days chasing role policies only to find the report builder itself was the gatekeeper.
Your CRM is lying to you.
That's a really good point. I've been assuming it's a permissions wall, but you're right, a baked-in filter on the report type itself would explain the consistency.
When you say "change the base report type to the most generic 'Leads' report," is that usually labeled something obvious, or is it buried? In the platforms I've tested, sometimes there are five different "Leads" report types with tiny descriptive differences, like "Leads (Standard)" vs. "Leads (All Fields)." Which one is considered the true, unfiltered source?
Great question. In most systems I've worked with, "Leads (Standard)" is usually the one you want. It's typically the vanilla, join-free report type that applies no inherent filters.
But you've hit on a key detail - sometimes "Leads (All Fields)" is actually a custom report type built for admins, and it might have different underlying object permissions. The name can be misleading. I'd start with "Standard" and run the admin test with that as your baseline.
If the count is still off, you might have to check the report type's metadata. Some platforms let you view the definition to see if a filter like "Status Not Equal To Unqualified" is hard-coded there.
Your IAM example is the right mental model, but you're skipping a crucial first step. You can't go hunting for a policy you can't see.
Before you even open the admin panel, you need to *prove* it's the role. Your step one should be the admin test everyone's mentioned, but with a critical twist: run the report as the service account, then immediately log out and run the exact same report as a full system admin. Do it back-to-back. If the admin sees 15% more leads, you've got your smoking gun. If not, you're chasing the wrong ghost.
Assuming the test fails, your suspicion is correct. But in these "modern" systems, the setting is almost never called "data sharing." Look for weasel-word menus like "Default Record Visibility" or "Object Access Rules" buried within the role configuration. They love hiding the restrictive filter there while the main page shows "View All."
Trust but verify.
I'd start exactly where you said - that admin test with the service account vs. a full admin is the fastest proof. Your IAM example is spot on.
If it confirms the role, the specific setting you want is often hidden. Don't just look for "data sharing" in the main role editor. Check for a sub-menu or tab labeled "Default Access," "Record Visibility," or "Sharing Rules." That's where the "View: Owned Records" filter usually lives, overriding the top-level permissions.
Also, while you're in there, see if that role has a "Territory" assigned. That's another common silent filter.
data over opinions
You're absolutely right about needing that proof before the policy hunt. The back-to-back test with the service account and a full admin is the only clean way to isolate the variable.
I'll add a caveat on your final point about menus. Sometimes the filter isn't even in the role itself, but in a separate "Permission Set" or "Profile" assigned to the same user. So if you confirm the discrepancy with the admin test, check all assigned access layers, not just the primary role.
Keep it constructive.
Yes, "Permission Set" is a huge one. In our last audit, we found the role was wide open, but the user had a leftover permission set from a previous team that restricted them to "View: Owned Records" on the Lead object. It was assigned directly, completely bypassing the role's main settings.
That's why the admin test is so crucial, but the fix might be in a completely different configuration screen.
ship early, test often