Skip to main content
Featured group

Data Quality & Management

Have questions about data import, quality, general management? Share your questions here!

Hi everyone,

I would appreciate some architectural guidance on a Salesforce security requirement.

We need to restrict Sales users from exporting or extracting company-wide financial/reporting data, while they must continue using Salesforce normally, including the Salesforce Mobile App.

Our initial approach was to remove the API Enabled

permission from their corresponding Permission Set Group to prevent API-based extraction tools such as Data Loader or spreadsheet connectors. 

Howeever, I would like to confirm with you, if API Enabled is also required for the Salesforce Mobile App, which Sales users need to use.

Therefore, my questions are:

  1. Is removing API Enabled an appropriate approach for preventing data extraction in this scenario, or would this have unintended consequences beyond the intended restriction?
  2. What would be the recommended Salesforce architecture to restrict API-based data extraction for Inside Sales while keeping access to the Salesforce Mobile App?
  3. Would API Access Control / Connected App allowlisting be a better approach, for example allowing Salesforce Mobile while restricting other API clients?
  4. Are there other Salesforce permissions or security controls that should be considered specifically for preventing report/data export without disabling API access entirely?

The business requirement is specifically to restrict data extraction

, not to prevent Sales users from using Salesforce Mobile or other approved Salesforce functionality. 

 

The focus is on:

  • Reports: No creating, editing, cloning, or exporting.
  • Dashboards: No access to restricted Turnover dashboards.
  • Subscriptions: Prevent receiving Reports/Dashboards via email.
  • List Views: Prevent export/print as an extraction method.
  • Folder Sharing: Prevent indirect access to restricted Reports/Dashboards.

Any guidance or documentation from Salesforce would be greatly appreciated. 

 

@Salesforce Administrators & Developers, @Salesforce Administrators and Developers, @APAC Architects, @Data Quality & Management

 

 

#Trailhead Challenges  #Trailhead  #Salesforce Developer  #Salesforce Admin  #Reports & Dashboards

3 answers
  1. Yesterday, 9:57 PM

    The Salesforce Mobile App doesn't need API Enabled, so removing it won't break mobile. Just check that no other tools your sales team uses, like Outlook/Gmail integrations or AppExchange apps, depend on it. 

    For extra control, use API Access Control to allowlist only approved connected apps like Salesforce Mobile. Also make sure users don't have "Use Any API Client" or "Approve Uninstalled Connected Apps." 

    For reports, remove the Export Reports permission and limit report subscriptions. The best protection is still sharing and field-level security: if users shouldn't extract financial data, they probably shouldn't see it at all. 

0/9000

Hi everyone,

I’m looking for some architectural input on a record visibility design for a multi-subsidiary arquitecture. 

 

Context:

  • Account and Quote OWDs are set to Private.
  • An ERP integration creates Quote records linked directly to QuoteAccountId (field when you activate the feature "Create Quotes without Opportunities"
  • All sales users share the same Profile and Role and public groups BUT
  • Field Sales users should only see Quotes linked to Accounts they own, provided the Quote and Account regions (e.g. Spain, France, etc.) match.

Current Solution:

To distinguish Field Sales users from back-office users, I created dedicated Public Groups per region (e.g. Group_FieldSales_RegionA, Group_FieldSales_RegionB).

A Record-Triggered Flow checks whether the Account Owner belongs to the corresponding Field Sales group. If so, the Flow creates a QuoteShare record for that user.

Importantly, these Public Groups created for the Field Sales users are not used in Sharing Rules

. They are only used by the Flow as an internal validation mechanism to identify whether the Account Owner is a Field Sales user. This is also important for managers with hybrid responsibilities, who may belong to both Field Sales and back-office groups (they are everywhere in every public group with execption of my newly created Field Sales public groups) 

 

Architectural Question:

There is a concern of non-developer members about using Public Groups and believes visibility should be based purely on Account Ownership.

  1. Would you consider using Public Groups as an internal identity/validation mechanism in Flow a sound architectural approach in this scenario?
  2. If Public Groups cannot be used, what would be a robust alternative for a Flow to identify Field Sales users, given that Profiles, Roles and public groups are shared and some users have hybrid responsibilities (Sales Managers are inside and outside at the same time)?

I’d appreciate your thoughts on the best practice for this design.

Thanks in advance! 

 

@Salesforce Administrators & Developers, @Salesforce Administrators and Developers, @APAC Architects, @Data Quality & Management, @Ladies Be Architects

 

 

#Trailhead Challenges  #Trailhead  #Salesforce Developer  #Salesforce Admin  #Solution Architects  #Architects  #Enterprise Architecture  #Flow  #Security

4 answers
  1. Oct 6, 4:13 PM

    @Lena Wong

    You're right the business rule doesn't care who owns the Quote. The risk comes from how the platform stores the share, not from the rule. 

     

    Where the Quote owner matters (platform mechanics, not business logic) 

    A Flow created QuoteShare is a manual share (row cause "Manual"). Salesforce deletes manual shares on a record when that record's OwnerId changes. Your Flow most likely triggers on events like Quote created or Account owner changed. A Quote owner change isn't one of those events, so: 

    1. The ERP (or a user or mass transfer) changes the Quote's owner.
    2. The platform silently deletes the share the Flow created.
    3. Nothing re-runs the Flow, so the Account Owner loses access even though the business rule says they should have it.

    Fixes, in order of preference: 

     

    • Add a trigger path on Quote update when OwnerId changes, so the Flow recreates the share.
    • Add a scheduled reconciliation that compares expected and actual shares. This also catches other drift.

    A related edge case:

    if the Account Owner is also the Quote owner, they already have access. The Flow should skip creating the share in that case, because inserting a share for the owner is rejected or ignored. 

     

    If the ERP never changes Quote ownership

    (it creates Quotes under a fixed integration user and never updates them), this risk mostly goes away, and the Account owner, region, and bulk-load gotchas are what remain. I can't confirm that from here, so it's worth asking the ERP team. 

     

    On the permission set groups

     

    If every sales user is in the same Permission Set Group, that group can't tell Field Sales from back-office, so it doesn't help as the identifier. Option 2 only works with a separate, standalone permission set (for example

    Field_Sales_Spain) assigned directly to Field Sales users and not included in the shared group. Hybrid managers just get that one assigned too.

0/9000

Hi everyone,

I need to perform a bulk security update to grant visibility over thousands of existing historical Quote records in our system.

The Setup:

  • OWD: Account and Quote objects are both set to Private.
  • We use the "Create Quotes Without Opportunity" feature, so thousands of existing integrated Quotes are linked to Accounts via the standard QuoteAccountId field.
  • Field agents are assigned to Regional Public Groups (e.g., OutsideSalesSpain, OutsideSalesFrance, etc.) matching their specific company/region field value (Spain, France, etc.).

The Requirement:

 

I need to run a one-time bulk update to grant

Read access over all existing historical Quotes to the Account Owner of the Account associated with those quotes, but ONLY if that Account Owner belongs to the corresponding Regional Public Group, and ONLY

if the Quote's region matches the Account's region. 

 

I already created a flow to handle this when quotes are being created, but the flow does not handle this visibility when there already exist Quote records in Salesforce. 

 

If I use Data Loader to solve this for historical Quote records, the solution would take long and would be cumbersome (probably manual efforts). Is this possible to solve with Apex?. We are talking about more than 20000 existing Quote records!.

Thanks for your advice! 

 

@Salesforce Administrators & Developers, @Salesforce Administrators and Developers, @APAC Architects, @Data Quality & Management, @Ladies Be Architects

 

 

 

 

#Trailhead Challenges  #Salesforce Developer  #Salesforce Admin  #Data Management

3 answers
  1. Oct 5, 5:07 PM

    Hi @Lena Wong

     

    Yes, this is possible with Batch Apex and would be more suitable than Data Loader for 20,000+ existing Quotes.

    • Query Quotes in batches and get the related Account Owner and Region.
    • Verify the Account Owner is a member of the corresponding Regional Public Group.
    • Check that Quote Region = Account Region.
    • For matching records, create QuoteShare records with Read access.
    • Handle existing share records/duplicates and test the batch in a sandbox first.

    Batch Apex + programmatic sharing would be my recommended approach for this one-time historical data update.  

      

    If this helps resolve your issue, please consider marking it as the Accepted Answer so it can help other Trailblazers as well. Thanks! 

0/9000

Hi everyone,

I’m looking for some advice on a Salesforce security/architecture scenario. 

 

We have a group of Sales users whose Account access is granted through criteria-based sharing rules based on the entity/company they work for

. For example, users belonging to different country entities receive access to the corresponding Accounts. 

 

These users are also assigned to a Permission Set Group that grants Read access to a number of financial fields on the Account object

. 

 

The business requirement is to restrict these users from creating or modifying Reports, exporting Report data, and accessing restricted company-wide financial Reports and Dashboards.

At the same time, they must retain their existing visibility when working with an individual Account they have access to, including the relevant financial information, Invoices and Orders.

This leads to a question around the same financial fields being available through different Salesforce surfaces.

If a user has FLS Read access to these Account fields so that they can see them on the Account Record Page:

Is there any standard/native Salesforce mechanism to prevent those fields from being available in List Views, while still allowing them to be displayed on the Account Record Page?

And regarding Reports:

If the user is allowed to run a centrally provided Report, is there a standard way to prevent the financial fields from being exposed through that Report while keeping the user's existing Account-level visibility? 

 

We explored using Custom Report Types to exclude these financial fields from the layout. However, if an Inside Sales user runs a centrally provided Report built on a report type that does

include these fields (because Outside Sales and Management users share the reports and need them), Salesforce will render the data for the Inside Sales user too, as long as their FLS is active. Salesforce does not support dynamic column masking or role-based field filtering within a shared Report Type. 

 

Or is the standard Salesforce security model that FLS applies globally across all these surfaces, with Report/Dashboard folder access and export permissions providing the only standard restrictions?

I’m interested specifically in the standard Salesforce capabilities, or is this only possible per custom and if yes, how? LWCs? 

Thanks! 

 

@Salesforce Administrators & Developers, @Salesforce Administrators and Developers, @APAC Architects, @Data Quality & Management

 

 

#Trailhead Challenges  #Salesforce Developer  #Salesforce Admin  #Salesforce  #Data Management

6 answers
  1. Sep 29, 7:25 AM

    Hi @Lena Wong

     

    Salesforce FLS applies globally, so if a user has Read access to a field, that field can generally be accessed through Record Pages, List Views, and Reports. There is no standard Salesforce feature to display a field on the Account Record Page while hiding the same field only from List Views or Reports. Custom Report Types can exclude fields, but they cannot dynamically hide fields based on the user's role when FLS grants access. Report and Dashboard folder permissions can restrict access to reports, but they do not provide field-level masking within a shared report. For surface-specific financial-data restrictions, a separate security/data model or a custom LWC/Apex solution would be required.

0/9000

Hi everyone,

I need an architectural sanity check on a security requirement. I want to avoid hitting platform limits down the road and would appreciate some feedback on whether this can be safely achieved using Schedule-Triggered Flows or whether Scheduled Batch Apex would be required.

Context

  • We are changing the Organization-Wide Defaults (OWD) of the standard Quote object from Controlled by Parent to Private.
  • We receive an integration feed from an external system that creates or updates up to approximately 80 Quotes per Account per day. Quotes can also be created manually in Salesforce.
  • Quotes contain two custom Account lookups: Sold-To and Bill-To.
  • We have both Quotes with and without Opportunities.

Requirement

  • The Account Owner of the Sold-To Account and the Account Owner of the Bill-To Account must have Read-Only access to the related Quote.
  • When the Account Owner changes, access to the related Quotes must be updated accordingly, including removing the previous owner's access and granting access to the new owner.
  • The required access is managed through QuoteShare records with RowCause = Manual.

Architectural Question

Given the expected Quote volume and the need to automate the insertion and cleanup of QuoteShare records following Account ownership changes:

  • Is this approach feasible using a standard Schedule-Triggered Flow, or would Scheduled Batch Apex be required to ensure scalability and avoid governor limits?
  • What would be the cleanest architectural pattern for handling the cleanup and creation of Manual QuoteShare records when Account ownership changes?

Thanks for your guidance!

 

 

@Salesforce Administrators & Developers, @Salesforce Administrators and Developers, @APAC Architects, @Data Quality & Management

 

 

#Trailhead Challenges  #Salesforce Developer  #Salesforce Admin  #Salesforce  #Flow  #Apex

3 answers
  1. Sep 26, 8:40 PM

    @sakshi nagpal

    , would you still recommend using APEX instead flow if the volume of quotes is of up to 80 per day approx. in general and not per account?. 

    @Debarshi Bagchi, sorry if I ask you, I would appreciate any guidance in this sense,

0/9000

Hi everyone,

I need some architectural advice on the best way to handle report and data access for a global team without turning role management into a maintenance nightmare.

 

  • We have report folders separated by countries: Country A Reports, Country B Reports, and Country C Reports.
  • In our Role Hierarchy, we have local roles at the bottom (e.g., Sales Rep Country A, Sales Rep Country B) and a high-level corporate role at the top called Global Management.

The Business Requirement:

 

Product manager users in their Country or Regional Level role complained not being able to access reports with data related to other countries, so we moved them to the

Global Management team for them to be able to see everything

. They require full visibility to access records (Accounts, Opportunities) globally, but they also need to open and view the reports inside the local folders of Country A, Country B, and Country C. 

 

The challenge:

 

We noticed that when we move a user from a local country role up to the

Global Management

role to give them global record visibility, they immediately lose access to some of the regional reports they could see before, or the reports show up completely empty for them. 

 

My Questions:

  1. How can we avoid constantly shifting users from one role to another just so they can see cross-country reports while maintaining access to their respective records? What is the cleanest architecture to decouple record visibility from report folder visibility?
  2. What is the Best Practice in Salesforce to solve this? Should we keep these global users in a specific role and grant them report folder access via a Public Group or a specific Permission Set?
  3. Why do some reports return empty or give errors to a user right after they are moved to a higher role in the hierarchy? Is it because of dynamic filters in the reports (like "My Team's Records" or "My Role" filters) that break when their branch changes?

Thank you so much for your help! 

 

@Salesforce Administrators & Developers, @Salesforce Administrators and Developers, @APAC Architects, @Data Quality & Management

 

 

#Trailhead Challenges  #Salesforce Developer  #Reports & Dashboards  #Solution Architects  #Sales Cloud

2 answers
0/9000

Hello Trailblazers,

I am facing a login loop issue with a System Administrator profile when trying to access the Salesforce Mobile App on an iOS device (iPhone), and I would appreciate your insights.

 

  1. I can log in into the desktop version perfectly using Windows Hello PIN as a registered Built-In Authenticator (Passkey).
  2. When trying to log into the Salesforce Mobile App on iOS, after entering the Username and Password, the app forces a Passkey verification.
  3. The native iOS is still prompting me to use a passkey and there is no chance to choose another verification option. Since the passkey was created locally via Windows Hello, the iPhone cannot resolve it, leading to a dead-end with no option to bypass or cancel.

What I tried so far:

  • I disconnected the Built-In Authenticator from the User's Advanced Details via desktop Setup.
  • Upon trying to log in again on the mobile app, it bypasses the old key but immediately demands the creation of a new Passkey.
  • If I attempt to use a Temporary Verification Code, the login attempt is blocked beforehand by the following error message: 

    "Problem Verifying Your Identity. To log in, you need both a higher access level and an identity verification method. Contact your administrator to gain login access."

Context & Constraints:

  • This is a production environment, so I cannot modify global organization settings (such as changing Session Security Levels or altering My Domain mobile browser behaviors) without a formal change control process. I need a solution targeted either at the user level or understanding why the Mobile API triggers this specific high-assurance restriction for this profile.

 

Has anyone encountered this specific behavior where the Mobile App demands a High Assurance MFA method that blocks the login completely before allowing alternative verification? Any workarounds at the user/permission set level would be highly appreciated.

Thank you in advance! 

 

@Salesforce Administrators & Developers, @Salesforce Administrators and Developers, @APAC Architects, @Data Quality & Management, @SecurityForce

 

 

#Trailhead Challenges  #Salesforce Developer  #Salesforce Admin

3 answers
0/9000

Hi everyone,

I have a question regarding the “Create Quotes without Opportunities” feature and a potential future requirement involving Orders.

 

After enabling the “Create Quotes without Opportunities” feature, Salesforce automatically provides the QuoteAccountId field on the Quote object. For Quotes created without an Opportunity, the Account relationship is stored in QuoteAccountId, while the standard AccountId is not populated.

 

Would it be technically possible to populate the standard AccountId field with the value from QuoteAccountId

for Quotes created without an Opportunity?. 

 

The reason for considering this is that Orders will be used in the future, and the standard Account relationship may be required for the Order process.

Would populating AccountId = QuoteAccountId be a supported and recommended approach, or are there any Salesforce limitations or implications we should consider when working with Quotes without Opportunities? or any workaround?.

Thanks for your guidance! 

 

@Salesforce Administrators & Developers, @Salesforce Administrators and Developers, @APAC Architects, @Data Quality & Management

 

 

#Trailhead Challenges  #Trailhead  #Salesforce Developer  #Salesforce Admin  #Salesforce  #Flow  #Data Management  #Apex  #Sales Cloud

2 answers
  1. Sep 24, 3:17 PM

     

    Short answer: you generally cannot (and do not need to) write QuoteAccountId into the standard Quote.AccountId field. On the Quote object, AccountId is a system-derived, read-only reference that Salesforce fills from the related Opportunity's Account. It is not createable/updateable through the UI, Flow, Apex or the API, so an update like AccountId = QuoteAccountId will be rejected or ignored. For quotes created without an Opportunity, QuoteAccountId (label "Account for Quote") is the field Salesforce intends you to use as the account relationship. 

     

    For your future Order process this is already covered: Salesforce's documentation for quotes without opportunities says associating the quote to an account is optional but recommended, because an account is required to convert the quote to an order. When you create an Order from such a quote, the account comes from the quote's account (QuoteAccountId), so you don't need Quote.AccountId populated. 

     

    Practical recommendations: 

    1. Make QuoteAccountId required (validation rule, e.g. ISBLANK(OpportunityId) && ISBLANK(QuoteAccountId)) so every quote can be turned into an Order. 

    2. After enabling the feature, open Object Manager > Quote > Fields > Account for Quote and grant field-level security to your profiles/permission sets. It is hidden by default, which is why it often looks unavailable in Flow, reports and Apex. 

    3. In reports, flows, sharing logic and Apex, use one source of truth, for example a formula field BLANKVALUE(QuoteAccountId, Opportunity.AccountId), so logic works for quotes with and without an Opportunity. 

    4. If you create Orders yourself via Flow/Apex (rather than the standard Create Order action), set Order.AccountId = Quote.QuoteAccountId and Order.QuoteId = Quote.Id. 

     

    References: 

    Enable Quote Creation Without a Related Opportunity:

    https://help.salesforce.com/s/articleView?id=sales.cpq_quotes_without_opportunities.htm&type=5

     

    Release note, Create Quotes Without a Related Opportunity:

    https://help.salesforce.com/s/articleView?id=release-notes.rn_sales_quotes_without_opportunities.htm&release=244&type=5

     

    Quote object reference:

    https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_quote.htm

     

     

    Assumption: the statement that Quote.AccountId is read-only is based on the Quote object reference field properties and commonly reported org behaviour; please confirm in a sandbox (e.g. attempt the update in a Flow or Developer Console) before designing around it.

0/9000

From what I understand thus far about content bodies, they're essentially the body of a file that is uploaded to SF. Documents are files that have been linked to a record, and attachments are files that have not been linked. Do I have that right? 

 

I am trying to figure this all out in terms of our storage usage. If content bodies are the file itself, why don't the counts match?  Do attachments have content bodies? Are documents just the metadata for the content bodies?  

 

#Files Storage

 

 

Storage Usage: Overlap?

 

 

2 answers
  1. Aug 19, 2:51 PM

    A couple of these are flipped, and sorting them out explains the mismatch. There are actually THREE different file models in Salesforce, and they store their bytes in different places: 

     

    1. Classic Documents (the Document object): these live in Document folders (the Documents tab) and are NOT linked to a record. People use them for things like email-template images, logos, mail-merge templates. The bytes are in the Document Body field. So it is the reverse of what you had, Documents are the ones NOT tied to a record. 

     

    2. Classic Attachments (the Attachment object): these ARE linked to a specific record, via ParentId (an Attachment must have a parent). The bytes live in the Attachment Body field. So Attachments are the record-linked ones. 

     

    3. Salesforce Files (the modern model): this is ContentDocument (the file) plus ContentVersion (each version of it, where the actual bytes live, in the VersionData field) plus ContentDocumentLink (which records it is shared to). A single File can be linked to many records, or none. This is what most 'content body' language refers to, the file binary is on ContentVersion. 

     

    On your specific questions: 

    - Do attachments have content bodies? Not in the Files sense. An Attachment stores its own bytes in its Body field, it is a separate legacy object, not part of the ContentDocument/ContentVersion model. 

    - Are documents just metadata for content bodies? No, a Document has its own Body field with the actual bytes, it is not a pointer to a ContentVersion. 

     

    Why the counts do not match: 

    - Storage is split into File Storage and Data Storage. Documents, Attachments, and Files (ContentVersions) all consume File Storage, while records consume Data Storage, so a record count and a storage-MB figure will not line up. 

    - Within Files, every ContentVersion (every version of a file) counts its own bytes, so one file with 3 versions consumes 3x. That alone throws off naive counts. 

    - A single File linked to 5 records is ONE ContentDocument with 5 ContentDocumentLinks, counted once for storage but it can look like 5 items in related lists. 

     

    For auditing, Setup > Storage Usage is the cleanest view, and for the Files model query ContentVersion (ContentSize) rather than counting links. 

     

    If this helps, please mark it as the Best Answer so it helps the next person. Thanks :)

0/9000

How to delete NPSP Data Import records before the final Import?

Ran NPSP Data Import and found some blank data was imported. The next day, deleted the records and checked from "All" that the records were deleted. Ran NPSP Data Import ensure the correct number of records were import. When trying to take the next step of Data Import, no records were found. 

Ran NPSP Data Import records and all the records were found but not available for Importing.

Can all the 2 days of records be completely deleted and start a new import?

 

@Fundraising

@Nonprofit Success Pack

@Salesforce.org System Administrators

4 answers
  1. Sep 9, 9:37 AM

    This was the solution that worked for me.  I had two thousand records needed to delete after I discovered a mistake in the data I was going to import.  I created a report on the NPSP Date Import object to include the records I wanted to delete.  Make sure you include the Salesforce Record ID, the Import ID will not work.  With Salesforce Record ID  in hand I used Dataloader to delete the import records,

0/9000