Files are stored in an object called ContentDocument. When a file is shared with a record or when it is directly uploaded to a record, Salesforce creates a ContentDocumentLink record in the system. ContentDocumentLink is a junction object between a ContentDocument (file) and a record. In order to share files with other records, you just have to create a ContentDocumentLink between the file and the target record. Read this Salesforce article to learn more about these objects.
ContentDocumentId and LinkedEntityId are the two main fields of the ContentDocumentLink object. ContentDocumentId represents the Id of the file and LinkedEntityId is the Id of the record that you want to share the file with. LinkedEntityId is a polymorphic relationship field. It means that it supports multiple objects. It can be a record, group, Salesforce CRM Content Library, or a Chatter user.
Let's suppose that there is a record with some related files. You are creating a new record and you want those files to be related to the new record that you are creating. In this case, since those files already exist in the system, you don't have to upload them again. You can use Salesforce Flow to share those files with the new record. When you do so, same files will be attached to multiple records. It is important to note that, when you delete the file itself, it will be removed from all of the related records. Consequently, delete the ContentDocumentLink records instead of deleting the file itself. Let's create a record-triggered flow to automatically share all the related files of an account when a new case is created. You can use also the other Flow types to perform this.
Using Record-Triggered Flow to Share Files
1- Create a Record-Triggered Flow and select Case as the triggering object. Select the trigger to run when the case record is created. You can select the other trigger options too. Select optimize for Actions and Related Records. This option will make it an After-Save Flow.
2- Since it is a Record-Triggered flow, you can use the $Record global variable to reach to all the field values of the new case record. Because of this, there is no need to perform a get for the related account. You can directly perform a get for the ContentDocumentLink records in order to find the related files of the account.
In order to find the files of the account record, drag and drop Get Records from the toolbox. Select ContentDocumentLink as the object and filter the records by the LinkedEntityId field. This field value should be equal to the AccountId of the case record. Don't forget to get all the results and not only the first one.
3- If there is no file under the account, then there is no need to continue. Add a Decision element to check if the system found any records in the previous step. If the ContentDocumentLink collection (flow automatically created it in the first step) is not null, it means there are files.
4- If there are ContentDocumentLink records, add a Loop element to iterate over each item in the collection variable. Read this post if you don't know how to work with collections in Salesforce Flow.
5- Create a record variable for the ContentDocumentLink object. Add an Assignment element to assign values to this variable. ContentDocumentId is the Id of the file and it should be equal to ContentDocumentId field of the current item from the loop. Then map the LinkedEntityId field to the Id of the new case.
6- Create a new ContentDocumentLink record variable like you did in the previous step. However, this time mark it as a collection. After assigning values to the ContentDocumentLink record variable, you have to add this single record variable to the new collection variable that you created. In order to do that, add a new Assignment element.
End the loop after this step.
7- At the end of the 6th step, you have a collection variable with the records that you want to create. Now it is time to create them. Add a Create Records element to create all of the ContentDocumentLink records at once.
You can create the records inside the loop too, but it consumes one DML limit for each record creation. Consequently, you might hit the DML governor limit of Salesforce. On the other hand, flow's performance becomes slower. It is recommended to work with a collection in order to create the records at once (like in this example). This way, it consumes only one DML limit.
At the end, your flow should look like this.
Action Time
Salesforce Time account record has 2 files.
After creating a new case record, you can see that those files are automatically shared with the case.
Additional Steps
There can be many files related to the account record and you want only specific ones to be shared with the case record. In this case, you can add a decision element inside the loop to check your condition.
Let's assume that you want to share the files that contain "Salesforce" in their title. Add a Decision element to check the current file's name. ContentDocumentLink doesn't contain the title of the file, but it has a lookup to the ContentDocument record. You can find the title from the related ContentDocument record.
If it contains "Salesforce", continue like you did before. If not, go back to the loop in order to continue with the next file in the collection.
At the end, your flow should look like this.
Action Time
Salesforce Time account record has 4 files but only 2 of them contain "Salesforce" in their title.
After creating a new case record, you can see that only 2 files are automatically shared with it.
What happens if there are files under the account record but none of them meets the criteria?
Nothing. As you can see on this post, when you perform a create operation for an empty list of records, flow doesn't fail. It just doesn't create any records.
Gosh I needed this!
Great!
Thank you so much! This is amazing! Exactly what I needed
Is it possible to do this without using the loop?
I created the flow but now it fails once activated due to duplicates..
Hi,
If you have just one record, then you can do it without a loop but why is it failing? Is it because you already have the same file related to the record? So that you cannot re-share it?
Same is happening for me, the files aren't shared to the record. They are only shared to the associated master account.
If I am looking to update the owner of all the files based on the owner of the case record how can I do that? So on my record for example case, I want to update all the files attached to the contact to change the owner to be be same as the owner of the case.
Hi,
You can use a record-triggered flow on Case that works after save. Just use an update records element for the contentdocument object.
For the assignment variable, what is the breakdown (what are you drilling into)
In the first assignment element I assign LinkedEntityId and ContentDocumentId to a ContentDocumentLink record variable. LinkedEntityId is the record Id that you want to share the file (in my example it is Case Id). ContentDocumentId is the Id of the file (content document record).
Then in the second assignment, I add this ContentDocumentLink record variable to a ContentDocumentLink collection variable.
Great resource. But it seems like It won’t create some records. So far I’ve tested it out with records with less than 200kb, and they would all create but one record I was tying to create was 500kb and that won’t create the record
Hi I love your content, it was very helpful.
My question:
I tried this out and it works but What if a new file is uploaded to the Account after a Case has been created. How would you go about that.
Hi Charles,
It is not possible to trigger a flow directly from ContentDocument or ContentDocumentLink record. Maybe you can put a logic on the account update or case update, it will search for the new files and share them with the case. Still, it is not a good solution because the file will not be shared unless the account or case is updated.
I would like to add a document every time a work order is created. This PDF document is located in Files. It is not located on a related record. How would you go about this?
Hi,
First perform a get to bring the id of the file (ContentDocument). Then create a ContentDocumentLink with ContentDocumentId=the id that you got AND LinkedEntityId=Id of the Work Order.
Hello Yumi
I did record trigger flow and next step was get record. LinkedEntityId equals $Record>Work Orders ID.
Where do you enter the ID of the actual file?
You should use the ContentDocumentId field for the Id of the file (content document).
HI Yumi,
Thank you very much for your article, it is so useful.
I'm in final stage and I'm struggling with a point.
In my case, I have files stored on a Candidate Record I would like to clone to the Job Applicant Record.
If I follow your steps, all I would need to do is asking FLows to get the Job Applicant ID that is on the Candidate Record.
The problem is that for one candidate, there might be multiple Job Applicants (i.e multiple applications). Thus my issue is, how can I tell SF to clone the files to all related Job Applicants since I can't store all their IDs in one field except a text field ?
Hi,
In this case you need to get all the files that are related to the job applicant records that related to the candidate record. Then you need to work with loop and assignments. The tricky part is to do this get. You can use this custom action from unofficialsf: https://unofficialsf.com/a-graphical-soql-query-builder-for-flow/
It lets you run a complex query like this:
Select ContentDocumentId FROM ContentDocumentLink WHERE LinkedEntityId IN (Select Id FROM Job_Applicant__c WHERE Candidate__c={!Record.Id})
Hello,
First of all - thank you for sharing your knowledge. I've created the flow and in my case I work with Parent and Child Case, I share files from parent to child one. Although it works only when I create a Case manually. After I used a Case created by email2case it does not see the files attached to the parent case. They are located in the Files related list, so I think they should be considered as ContentDocuments, but they are not somehow. Have you got any idea why it happens?
Hi Olga,
Do you want to share the files from parent case to child case? In email2case, how do you relate the child case to the parent? Manually?
HI Olga, I am looking for same solution. By email2case seems, that files are only related to Email Message, so I build Flow, where "Email Message is created" is trigger, but somehow this is not working.
Hi,
It can be that the email message is created first and then after a very short time, the files are created.
Can you add a scheduled path to your flow? So that it will run after a few seconds. This is just an idea but I think it worths trying.
Hi,
IN My Scenario, I need it the File to Assign it to a PUBLIC GROUP "ABC" with a Collaborator Access, how do I do it with Flow?
Hi,
You can share with a chatter group but I think it is not possible to share with a public group.
Hi, I need to get the file names for the users to choose from the opportunity object, how can we do that?
Hi,
First get content document links that are related to the opportunity (according to the LinkedEntityId field). This object doesn't have the file names, so it won't be enough. Create single and collection Content Document variables (one of each). Do a loop and assign the ContentDocumentId and Title values to the ContentDocument variable (using the values of the ContentDocumentLink records). Then of course, add the single Content Document variable to the collection variable. So at the end, you will have a collection of ContentDocuments. Use this collection for the choices in your picklist/radio button/checkbox group etc.
I tried it now and it works.
Hi Yumi,
I am trying to copy attachments from Case to another Work Order however the files attached to the case are linked to the EmailMessage (attachments from Email To Case) and not the Case itself so I am unable to select the attachments in the Get Records step.
Regards,
Samir
Hi Samir,
You can get the emailmessage records that are related to the case. Then get the files that are related to these email messages. In this case, you can use the new "In" operator.
I am finding it difficult to create the New Content Document records and sharing them with a new case.
We are using the Email to Case process and any files attached to the Email are not shared when a new Case is created. If files are uploaded these are shared with the new case
Hi Angela,
Is your flow working on the case record or email message record? I think it is not finding the files when the flow runs. Can you try to run it one minute after the case is created?
Hello!
I'm trying to use this model, but my files are in cases.
I need to have this files in opportunity too, because the cases are created automatically with the files when user send the case out of salesforce.
So what I need is:
Files stored in Attachments of the object CASE needs to be shared with the Opportunity.
Can you help me?
Hi Monique,
Yes it is possible. I think you need a record-triggered flow that will work when the case is created. It should get the related files (through ContentDocumentLink) and then share them with the opportunity. How will you know which opportunity to share? Do you have a connection between case and opportunity?
Hello! Thanks for replying me.
Well, that's a good question for a beginner on Flows. ahaha.
I was looking for it, and I didn't find anything that connect case with opportunity.
I'm trying to figure it out, but I have no idea how to procedure.
Hi,
Excellent solution, much appreciated. By any chance, is there a way to delete the files that I copied? In that case, I want to "cut" the files, instead of copying then, so I don't have same files in two different objects.
Thanks
I think you can delete the existing ContentDocumentLink that is related to the record (through LinkedEntityId).
Hi Yumi. How would i go about retrieving specific attachments from a record (opportunity) and including them as attachments in an outbound email? I'd love to avoid a 3rd party installed package if possible.
Thanks!
Hi Nick,
Current options to send an email from flow are email alerts and send email action. However, they don't support sending attachments.
There is a free package that you can install from unofficialsf.com which lets you send attachments. You can get the content document links and provide them (collection) as an input parameter to the action.
Content Document Link error - Not able to add more than 1 file .
If I am adding one file on Opportunity then it is creating link on Order for that file but if I want to add another file on Opp and trying to trigger the flow then this is the error I am getting "Error Occurred: This error occurred when the flow tried to create records: DUPLICATE_VALUE: Document with ID: 06923000001WkWp is already linked with the entity with ID: 80123000002qkCL. You can look up ExceptionCode values in the SOAP API Developer Guide."
Can someone help me up with this ?
Hi,
It looks like the flow is trying to share the same file again (it is trying to create a content document link record for the same content document).
Is there any way in this flow to check for duplicate files being linked to the Child record. For example, if a user wanted to pull down records again after the first time, we would only want newly added files to the parent record to be pulled down.
Hi,
I think you should first get the existing files and assign their ids to a text collection.
Then, when you do a loop for sharing the new files, add a decision to check if that ids collection contains the id of the current file. If it doesn't contain, it means that it is a new file, so that you can share it.
I'm getting this same error: DUPLICATE_VALUE: Document with ID: 0691K00000xU07I is already linked with the entity with ID: 0051K000009wm6F.
I did a check for duplicates by only pulling ContentDocumentLinks that not already related to the entity in question (0051K000009wm6F), but the error persists.
Hi,
When you check for the duplicates, are you checking by the content document id?
Hi Yumi,
Thanks for this post, really appreciate your effort. For my use case, I have E2C and when a response is sent on a case closed for more than 7days, I should create a new case and have the email along with the attachment on this new case. I have followed similar steps as this post, however only new email message record is getting linked to the case but not the files that are sent as attachments.
Hi,
Can you try to use the async path in your flow? I think when your flow runs, the files are not there yet, so it doesn't find the files. Putting a little bit of delay (using the async path or a scheduled path that waits for a minute) can solve the issue.
Hi! great solution!
I want to add a desicion in the loop that checks if the file already exists in account. if exists, don't copy the file to the account. how can i do that?
Hi,
You can get the content document link records related to the account. Then prepare a text collection of content document ids (using a loop and assignment). After that, check if the current file's Id is included in that text collection. If yes, it means that it is already under the account.
Read this post to see how to prepare a text collection of Ids.
https://salesforcetime.com/2022/12/05/how-to-use-the-in-and-not-in-operators-in-flow/
I used this blog to create a Flow that when an Inbound Email is received on a Closed Case - A new Case is created and the Files are shared.
This works on the whole, but what I am finding is that
1. If a File has been uploaded to the Case - it will be shared with the new Case
2. If the File has been attached in an Email - It will not be shared with the new Case.
In my Email to Case settings I have checked - Attachments should be stored as Files, so I do not understand why some Files are shared and others are not
Can you try to run your flow asynchronously? I think first the email message is created and then files are attached. So it can be that your flow runs before the files are attached.
Hi Angela,
I am having the same issue. Did you happen to find a solution to this? I'd love to know!
Thank you!