Review Invocable Apex Use Cases
Practical patterns for combining Folio invocable actions in Salesforce Flow. Start with the chaining recipes for the variable mappings you’ll use repeatedly, then read the business scenarios for end-to-end examples grouped by action.
If you haven’t read the per-action reference yet, start with Automate with Invocable Apex. For failure handling, see Handle Invocable Apex Errors.
Get-then-Use recipes
Most real flows look up Documents first, then act on them, which is exactly what the Get Documents and Get Document Shares actions were created for. First retrieve what you need, then pass the output into another invocable to perform an action on those records. The patterns below are the chains you’ll use repeatedly. In each one, the documentIds Text Collection from Get Documents maps directly into the next action’s documentIds input.
Most Folio invocable inputs require Text Collection variables. Inputs like
shareWithIds,recordIds, andbyParentRecordIdsare typed as Text Collections and only accept a Flow Text Collection variable. They will not accept simple Text variables, Constants, formulas, or any other variable type. Declare a Text Collection variable, add one or many values to it via an Assignment element, then pass that variable into the action.
Get Documents, then Share them
Look up Documents by filter conditions, then grant a set of users or groups Read or Edit access to all of them in one step.
filter by parent record, owner, Tag, or title"] -->|documentIds| Z["Share Document
+ shareWithIds: Text Collection of User and/or Group IDs
+ accessLevel: Read or Edit"]
Get Documents, then Apply a Tag to them
Look up Documents by filter conditions, then apply one or more Tags to all of them so they can be grouped, searched, and filtered together on the Folio Docs home page.
filter by parent record, owner, Tag, or title"] -->|documentIds| Z["Apply Tag to Document
+ tagNames: single Text, comma-separated"]
tagNamesis a single Text value, not a Text Collection. Pass one comma-separated string —Priority, Renewal, Q3— not a Flow Text Collection. This is the one Folio input that breaks the collection convention. Existing Tags are matched by name and reused; unmatched names create a new Tag.
Idempotent — safe to rerun if the flow loops or retries.
Get Documents, then Link them to Salesforce records
Look up existing Documents by filter conditions, then attach them to one or more additional Salesforce records so they surface on those records’ pages too.
filter by parent record, owner, Tag, or title"] -->|documentIds| Z["Link Documents to Records
+ recordIds: Text Collection of Salesforce record IDs"]
The target object type must be configured as a Linkable Object in the Admin Panel.
Get Documents, then Transfer their Owner
Look up Documents by filter conditions (typically by current owner), then bulk reassign ownership to a different user, optionally retaining Read or Edit access for the prior owner during the transition.
filter by current owner (or any other filter)"] -->|documentIds| Z["Transfer Document to Owner
+ newOwnerId: User ID of the new owner
+ priorOwnerAccess: Read, Edit, or None"]
Get Documents, then Clone them
Look up Documents by filter conditions, then create copies of all of them — optionally with their relationships preserved and ownership reassigned to a different user — so a team has pre-populated starting points instead of blank Documents.
filter by parent record, owner, Tag, or title"] -->|documentIds → sourceDocumentIds| Z["Clone Document
+ cloneRelationships: true / false
+ newOwnerId: User ID of the clone's owner (optional)"]
Get Documents’ documentIds output feeds Clone Document’s sourceDocumentIds input directly — no Assignment or transformation element in between. It’s the most natural pairing in the whole action set.
Set the clone flags you actually want. All four of Clone Document’s optional inputs default to False, so a bare call copies content only — no Tags, no shares, no linked records. For a “pre-populated starting point” use case you almost always want Clone Relationships set to True, and often Clone Tags as well.
newDocumentIds comes back in the same order as sourceDocumentIds. That positional guarantee is what makes this chainable: index n of the output is the clone of index n of the input, so you can correlate each clone with its source in a downstream loop.
If you only want to clone the most recent matching Document, pair this with a Get Records step on folio__Document__c ordered by LastModifiedDate DESC and capped to 1 — Get Documents itself does not provide a sort/limit interface.
Clone Documents, then Share, Tag, or Link the new ones
Clone Document returns newDocumentIds. Use it as the documentIds input on any modify-action — share the new clones with a group, apply Tags, link them to additional records, etc.
Share Document, Apply Tag to Document,
or Link Documents to Records
+ remaining inputs for the chosen action"]
The same pattern works for Create Document from Template — feed its newDocumentIds into any modify-action’s documentIds.
Get Document Shares, then downgrade everyone to Read except the Owner
Look up existing share rows on a set of Documents, filter the result down to rows where the user is not the record owner and the access level is Edit, then update those rows to Read access. Useful for locking down a Document set after a project closes, or for enforcing a periodic access review.
Because Share Document is additive only and never downgrades, the actual downgrade has to be performed via a Salesforce Update Records step on the folio__Document__Share records returned by Get Document Shares.
byDocumentIds"] -->|documentShareRecordsNoOwners| F["Filter in Flow
AccessLevel = Edit"] F -->|matching share rows| Z["Update Records (Salesforce standard action)
Object: folio__Document__Share
Set AccessLevel = Read"]
Use the
documentShareRecordsNoOwnersoutput and skip the owner filter. That output already excludes rows whoseRowCauseisOwner, so there’s no need to compareUserOrGroupIdagainstOwnerIdin Flow. Owner share rows can’t be modified by anyone, and including them fails the Update Records step.
Get Document Shares, then revoke access entirely
Remove access outright rather than reducing it — the standard offboarding or access-review pattern.
byDocumentIds
(optionally byUserIds to target specific people)"] -->|documentShareRecordsNoOwners| Z["Delete Records (Salesforce standard action)
Object: folio__Document__Share"]
Deleting the share row removes the access outright, rather than reducing it as the downgrade recipe above does.
Use the No Owners output. Owner share rows can’t be deleted, and including them causes the Delete Records step to fail.
Note what this does and doesn’t reach: access granted by other means — auto-share from record ownership, team sharing, or Salesforce sharing rules — is recreated by the platform or the package and will reappear. This recipe removes explicitly granted shares. See Update Data in Bulk for the underlying schema and Handle Invocable Apex Errors for failure handling.
Get Document Junctions, then remove a Tag from Documents
Unlink a Tag from a set of Documents without touching the Tag itself.
junctionType = Tag
+ byTagNames"] -->|junctionRecords| Z["Delete Records (Salesforce standard action)"]
Deleting a Tag junction unlinks the Tag from that Document without touching the Tag record itself, or any other Document that carries it.
For a one-off cleanup, the Tags tab does the same thing in the UI, with merge and replacement-tag handling built in. Use this recipe when the removal needs to be automated or conditional. See Update Data in Bulk for the Junction schema.
Get Document Junctions, then unlink a record
Remove a Document’s link to a Salesforce record.
junctionType = record link
+ byDocumentIds and/or byLinkedRecordIds"] -->|junctionRecords| Z["Delete Records (Salesforce standard action)"]
Linking is additive — Link Documents to Records never removes an existing link — so unlinking is always a separate deletion step. There is no “unlink” invocable, and this is why.
Every other relationship on the Document — Tags, other linked records — is untouched. This is the deletion half of Re-link Documents when an Opportunity moves Accounts; pair the two when a record needs to move rather than merely gain a link. See Update Data in Bulk and Handle Invocable Apex Errors.
Create Documents from a Template in bulk
Instantiate a template across an entire collection of records in a single call.
(or any collection of record IDs)"] --> B["Transform
into a Text Collection"] B -->|sourceRecordIds| C["Create Document from Template
+ templateId"] C -->|newDocumentIds| Z["Any modify-action
Share, Tag, or Link"]
sourceRecordIds is a Text Collection, and the action creates one Document per source record — so a single call can instantiate a template across a whole set of records. Merge fields resolve per record.
newDocumentIdscomes back in the same order assourceRecordIds, so index n of the output corresponds to index n of the input. That positional guarantee makes the results correlatable in a downstream loop, exactly the way Clone Document works.
The template must be in Active status — see Template Status. You’ll also need its ID, available from Copy ID on the template’s row in the Templates tab.
Common chaining mistakes
- Empty/null collections. Passing an empty
documentIdsfrom an upstream Get is a no-op, but make sure your Decision node checks for emptiness so the flow logs reflect “no Documents matched” rather than silently doing nothing. - Wrong ID type.
shareWithIdsaccepts User IDs and Public Group IDs only. The error is explicit: “Only public groups (Type = “Regular”) are allowed as share targets… Queues, Roles, Role-and-Subordinates groups, and Customer Portal groups are not supported here.” It names the offending group, its ID, and its type. - Draft or Archived template IDs. Only templates in Active status can instantiate a Document. Passing a Draft or Archived
templateIdfails at runtime, not at design time — a common surprise when a template is built and tested but never activated. See Template Status. - Sharing with inactive or unlicensed users.
shareWithIdsvalidates every user target: each must be active and hold the Folio Docs User permission set. In the custom user lookup recipe, a stale lookup pointing at a departed user fails the whole call. Add a Decision or a Get Records filter onIsActivebefore building the collection. The same active-plus-licensed rule applies tonewOwnerIdon Transfer, Clone, and Create from Template. It does not apply topriorOwnerAccess— retaining access for a now-deactivated prior owner is fine. - Overbroad filters. Leaving every Get Documents filter empty returns empty (by design). Always supply at least one filter.
- Mixing record-collection loops with text-collection inputs. If you have a Record Collection from a Get Records step, use a Transform element to map the record IDs into a Text Collection — don’t try to map the record collection directly into a
documentIdsslot.
Business use cases
The recipes below are examples of real business problems and how Folio’s invocable Apex actions can be combined in Flow to solve them. Each example highlights a primary action, but most production flows chain several together — use these as starting points and adapt the inputs and triggers to fit your org’s processes.
Account Plan refresh
Functionally, this flow freezes last year’s Account Plan, spins up a fresh one from a clone of it, and notifies the owner to start updating the new copy — all 90 days before the Account’s renewal.
- Trigger:
Scheduled flow that runs daily and identifies any Account whose renewal date is exactly 90 days out. - Step 1 — Find the most recent Account Plan.
Call Get Documents filtered bybyParentRecordIds = {Account Id}andbyTags = ["Account Plan"]. Use a follow-up Get Records onfolio__Document__cordered byCreatedDate DESCand limited to 1 to pick the most recent matching Document. - Step 2 — Clone it into a new Account Plan.
Call Clone Document withsourceDocumentIds = {That Doc Id},cloneTags = trueandcloneRelationships = trueso the new clone inherits theAccount PlanTag and the existing Account record link, andcloneSharing = falseso the new Document starts shared only with the owner by default (plus any Account Team sharing that applies automatically). - Step 3 — Lock down the original.
Use the Get Document Shares, then downgrade everyone to Read except the Owner recipe on the original Document so the prior year’s plan can no longer be edited by anyone except the Owner. Note this doesn’t technically prevent the Owner from editing the Document. If the original truly needs to be fully locked, transfer it to a Salesforce Administrator via Transfer Document to Owner withpriorOwnerAccess = "Read"so the Account owner retains read access but loses edit access. - Step 4 — Mark the original as historical.
Use Salesforce Update Records on the original Document to prependOld —(or your preferred convention) to itsfolio__Title__cso it’s clearly archived. Optionally apply aHistoricalTag as per your organization’s preferences using Apply Tag to Document. - Step 5 — Email the owner.
Pull the new Document’s Document Deep Link field and email it to the Account owner. The Document Deep Link opens the new Document directly inside the Folio Docs home page, so the owner doesn’t have to hunt for it.
Create a Close Plan on Stage change
When an Opportunity moves to a late-stage like Legal Negotiation, sales reps benefit from a structured Close Plan tied to the deal. This pattern instantiates a Close Plan Template the moment the Opportunity hits that stage, sets the Opportunity owner as the Document owner, and emails them a deep link to start filling it in.
-
Trigger:
Record-triggered flow on Opportunity (StageNamebecomes “Legal Negotiation”). -
Step 1 — Instantiate the Close Plan Template.
Call Create Document from Template withtemplateId = {Close Plan Template},sourceRecordIds = {Opportunity Id}(Text Collection of one), andnewOwnerId = {Opportunity.OwnerId}. The Close Plan Template should have aClose PlanTag configured on it so the new Document inherits the Tag automatically. With the right Admin Panel settings, the rest of the linking and sharing happens for you with no extra Flow steps:- The new Document is default-linked to the Opportunity (the Source Record).
- If Auto-link from Opportunity to Account is enabled, the package also links the new Document to the Opportunity’s parent Account.
- If Auto-Share Level with Record Owner on the Account Linkable Object is set to Read or Edit, the Account Owner is automatically granted that level of access on the new Document as well.
Net effect: the Document is created from the Template, linked to both the Opportunity and the Account, and shared with both the Opportunity Owner and the Account Owner — all automatically.
-
Step 2 — Email the Opportunity owner.
Send an email to the Opportunity owner prompting them to complete their Close Plan by the target close date. Include the new Document’s Document Deep Link field in the email body so they can click to easily access the Document on the Folio Docs home page without hunting for it. -
Business outcome:
Every late-stage Opportunity has a structured Close Plan ready for the rep the moment they need it, pre-tagged for downstream filtering and reporting on the Folio Docs home page.
Share Case Documents on escalation
- Trigger:
Record-triggered flow on Case (IsEscalatedbecomestrue). - Inputs used:
documentIds = {Documents linked to Case},shareWithIds = {Support Manager Group ID},accessLevel = "Edit". - Action sequence:
Get Documents (byParentRecordIds = {Case Id}) → Share Document. - Business outcome:
Escalation managers get immediate access to all Case context without manual sharing.
Share Documents with users in a custom user lookup field
It’s common practice to track role-specific ownership on a record using a custom user lookup that’s distinct from the standard Owner — for example a Customer Success Manager, Solution Consultant, Implementation Manager, or Renewal Manager on an Account, Opportunity, or Case. Because these users aren’t the record Owner, Folio’s built-in auto-share rules and Account Team sharing don’t always cover them. To grant read or edit access to the user sitting in any custom user-lookup field, use the pattern below.
- Trigger:
Record-triggered after-save flow onfolio__Junction__c(the object that links a Document to a record), firing on Created events. - Step 1 — Filter to the right parent object.
Add a Decision element that checks whether the new Junction’s parent record ID begins with the Salesforce key prefix for the object you care about (e.g.001for Account,006for Opportunity,500for Case). If not, exit the flow. - Step 2 — Look up the parent and the custom user-lookup field.
Use a Salesforce Get Records on the parent object, filtered by the Junction’s parent record ID, retrieving the custom user-lookup field(s) you want to share with — Customer Success Manager, Solution Consultant, Implementation Manager, Renewal Manager, or any other role-specific user lookup. - Step 3 — Share the Document.
Build a Text Collection containing the user IDs from those lookup fields, then call Share Document withdocumentIds = {The Junction's Document Id},shareWithIds = {User Id Collection}, andaccessLevel = "Edit"(or"Read"depending on your access policy). - Business outcome:
Every Document attached to the parent record is shared with the role-specific users the moment it’s linked, without manual sharing or relying on Account Team membership. The same pattern works for any custom user-lookup field on any Linkable Object.
Tag Account Documents by industry
- Trigger:
Record-triggered flow on Account (Industryfield change). - Inputs used:
documentIds = {Documents linked to Account},tagNames = "{Industry name}". - Action sequence:
Get Documents → Apply Tag to Document. - Business outcome:
Documents become filterable on the Folio Docs home page by industry without manual tagging.
Re-link Documents when an Opportunity moves Accounts
- Trigger:
Record-triggered flow on Opportunity (AccountIdchange). - Inputs used:
documentIds = {Documents linked to Opportunity},recordIds = {New AccountId}. - Action sequence:
Get Documents → Link Documents to Records. - Optional — remove the link to the prior Account at the same time.
Linking the Documents to the new Account does not remove the existing link to the prior Account, so by default the Documents stay linked to both. If you want the move to also drop the old link, follow the link step with a Salesforce Get Records onfolio__Junction__cfiltered by Document = {Documents linked to Opportunity} AND Linked Record = {Prior AccountId}, then Delete Records on the returned Junctions. Every other relationship on those Documents (Opportunity, Tags, other linked records) is left untouched. - Business outcome:
Existing Opportunity Documents now appear in the Folio Document Editor component on the new Account’s record page — and, if you opt into the cleanup step, no longer appear on the previous Account’s record page.
Reassign Documents when a rep leaves
- Trigger:
Screen flow run by an Ops admin from the user’s record page. - Inputs used:
documentIds = {Documents owned by departing user},newOwnerId = {New owner User Id},priorOwnerAccess = "Read". - Action sequence:
Get Documents (byOwnerIds = {departing user}) → Transfer Document to Owner. - Business outcome:
Ownership transitions cleanly with a Read window for the original owner during handoff.
Create a Sales-to-CS Handoff on close
- Trigger:
Record-triggered flow on Opportunity (StageNamebecomes “Closed Won”). - Inputs used:
templateId = {Sales-to-CS Handoff Template},sourceRecordIds = {Opportunity Id},newOwnerId = {Assigned CSM Id}. - Action sequence:
Create Document from Template → Apply Tag (Handoff) → Share Document with the CS team Group. - Business outcome:
A pre-filled handoff doc is waiting for the CSM the moment the deal closes.
Keep Documents current when a custom object changes
- Trigger:
After-save record-triggered flow on a custom object (e.g.,Project__c), configured for Updated and/or Deleted. - Inputs used:
$Record→ Record (New State);$Record__Prior→ Record (Prior State). In a Deleted flow, also set Record Was Deleted = True. - Action sequence:
Folio: Refresh Document from Record Changes. - Prerequisite:
The object must be configured as a Linkable Object. For the owner re-sharing half of this to do anything, its Auto-Share Level with Record Owner must be Read or Edit — with auto-share set to None, field updates still propagate but no sharing is applied. - Business outcome:
Every Document linked to the project stays current with the project record — names on Record Links, and values in Related Lists, Workbenches, Status Bars, Record Previews, and Kanban tiles all update live for anyone viewing. When the project changes owner, linked Documents are re-shared to the new owner at the configured level. - Note:
This action replaces the former Apply New Owner Sharing, which only handled the sharing half. It is also the only Folio invocable that lives in a flow on the linked record rather than a flow about Documents. Account, Contact, Opportunity, and Case need no flow at all — they ship with packaged triggers. See Set up Real-Time Updates.
Clean up Documents when a parent record is retired
- Trigger:
Record-triggered flow on the parent object when a status field moves to a terminal value (e.g.,Project__c.Status__c = 'Cancelled'). - Inputs used:
Text Collection of Document IDs from Get Documents filtered by the parent record. - Action sequence:
Get Documents → Folio: Delete Document. - Business outcome:
Documents tied to the retired record are removed — or archived, if hard delete is disabled, which is the default. Using the invocable rather than a raw Delete Records element is what makes that distinction work, and what keeps Junctions from being orphaned. See Updating and deleting Folio data. - Note:
Delete Document returnsarchivedInsteadOfDeleted(Boolean) alongsidesuccess. Branch on it to tailor what happens next — logging, notifying, or a different follow-up when the org’s Allow Document Hard Delete setting caused an archive rather than a delete. This is how a flow tells the two outcomes apart, since both returnsuccess = true.
Audit which Documents carry a given Tag
- Trigger:
Scheduled flow, or an on-demand screen flow. - Inputs used:
junctionType= Tag links;byTagNames= the Tag(s) you’re auditing. - Action sequence:
Folio: Get Document Junctions → loop / report overjunctionRecords. - Business outcome:
A list of every Document carrying a governance-relevant Tag. - Note:
Take thejunctionRecordsoutput rather thanjunctionIds— it carries the field values a report needs, with no separate Get Records step. To remove the Tag rather than report on it, see Get Document Junctions, then remove a Tag from Documents.
Related: Automate with Invocable Apex · Handle Invocable Apex Errors · Manage Templates · Set up Real-Time Updates · Update Data in Bulk