October to November 2025
Case Study: Partner List View Automation in Salesforce
Delivered as a sub-vendor for an enterprise customer of a Salesforce consulting partner, October to November 2025.
Context
A Salesforce partner portal gives external partner companies visibility into their own account data, such as opportunities, cases, or orders. When more than one partner account shares the same portal, each one should see only its own records, in list views that make sense for that account. Between October and November 2025, we worked as a sub-vendor on this kind of setup for an enterprise customer of a Salesforce consulting partner. The org had one shared portal, a growing number of partner accounts, and a standard set of list views that every partner account needed, scoped to its own data across several objects.
The problem
Setting up scoped list views for one partner account by hand is not hard the first time. Doing it for dozens of accounts, then keeping all of them consistent as the shared templates change, is where a manual process falls apart. Every new partner account meant an admin building a set of list views by hand, with the right filters and the right sharing, one object at a time. When a template list view changed, a renamed field, a new filter, an added column, every account already set up needed the same change repeated manually. There was no single place to make a change once and have it apply everywhere, so the setup drifted account by account until it was hard to know which version of a list view a given partner was actually looking at.
What we built
We built an automation layer that treats list view provisioning as a repeatable process instead of a manual task.
Account and user triggers watch for the signals that start the process. When an account is flagged as a partner account, a trigger creates a public group for that account and starts list view provisioning for it. When a portal user is added or activated, a trigger finds their account's group and enrolls them in it, so access follows the account automatically.
The provisioning itself runs through queueable jobs, so it happens asynchronously instead of inside the same transaction as the account or user change that triggered it. Those jobs read a set of template list views through Salesforce's Metadata API and clone them for the specific account, applying an account-specific filter and account-specific sharing to each clone. That is what keeps a partner user looking only at rows tied to their own account.
Two batch jobs cover the bulk cases. One rebuilds list views for every existing partner account, useful for an initial rollout or a full resync. The other runs when a template list view changes: it looks back over a configurable window, finds the clones tied to the templates that were edited, deletes them, and requeues the rebuild so every affected account gets a fresh, correct clone. This second batch can also run on a nightly schedule, so template edits made during the day are picked up automatically instead of depending on someone remembering to trigger a rebuild by hand.
None of this is hardcoded to specific objects or list views. Which objects get cloned, which template list views to use, and how large each batch should be are all configuration, held in custom metadata records and custom labels that an admin can change without a deployment. Adding a new object to the automation, or adjusting how many accounts a batch processes at once, becomes a configuration change rather than an engineering request.
Because the cloning goes through the Metadata API, and because queueable and batch jobs run without a normal user session behind them, the automation authenticates through a Named Credential paired with an Auth Provider, so asynchronous jobs can call the Metadata API reliably rather than depending on a session id that is not available in that context.
On the engineering side, the automation is built as roughly twenty Apex classes with accompanying tests, and it pays attention to the edge cases that matter once something like this runs in a real org. Group and membership operations are written to be idempotent, so re-running the process does not create duplicate groups or duplicate memberships. Recursion guards keep parent and child accounts from re-triggering each other's automation in a loop.
Outcome
The result is a partner portal where each partner account's list views reflect only that account's data, and where those views stay in sync as the underlying templates evolve, without an admin touching every account by hand. Adding a new partner account, or a new object to the automation, is a configuration step rather than a rebuild. When a shared template changes, the affected accounts regenerate automatically instead of drifting out of date. The real shift was from a manual, account-by-account process to a configurable, self-correcting one.
What we'd do next
The next step is better visibility into what the batch and queueable jobs are doing: a simple log of what was provisioned, skipped, or retried on each run, so an admin can see the state of the automation directly instead of inferring it from the underlying list view metadata. That matters more as the number of partner accounts and templates grows, and it is the natural next investment once the core automation is stable.
