[SalesForce] Insert Content and link it programmatically using SOAP API

I'm trying to find a way how to upload content document and link it to Salesforce object (say, an opportunity) programmatically (using SOAP API).
That's what I tried:

  1. Insert new ContentVersion with base64 encoded VersionData, Title and PathOnClient
  2. Find ContentDocument related to ContentVersion just created "Select ContentDocumentId FROM ContentVersion WHERE Id = taken from step 1"
  3. Insert new ContentDocumentLink with ContentDocumentId = "taken from step 2", Share Type = "I" (inherited from Opportunity), LinkedEntityId = "my opportunity id"

All the operations completed successfully. I can see the Opportunity with linked file in Lightning Experience, however, can't see the file in SF Classic and I can't see it in my private library either. But I can navigate to the Content using its Id (so it's definitely created in Salesforce).

  • Pic1 shows (LEX) Opportunity and linked files.
  • Pic2 shows (SF Classic) same opportunity but no files are visible.

Did I miss any important step?

Content document

Opportunity

enter image description here

The source code (C#, Web Reference using Partner WSDL. Login, error handling is omitted):

// create content version
XmlDocument docCreateContentVersion = new XmlDocument();
XmlElement[] xmlCreateContentVersion = new XmlElement[3];
xmlCreateContentVersion[0] = docCreateContentVersion.CreateElement("VersionData");
xmlCreateContentVersion[0].InnerText = System.Convert.ToBase64String(Encoding.ASCII.GetBytes("Hello, World")); 
xmlCreateContentVersion[1] = docCreateContentVersion.CreateElement("Title");
xmlCreateContentVersion[1].InnerText = "My Test Document";
xmlCreateContentVersion[2] = docCreateContentVersion.CreateElement("PathOnClient");
xmlCreateContentVersion[2].InnerText = "My Test Document";
sObject createContentVersion = new sObject();
createContentVersion.type = "ContentVersion";
createContentVersion.Any = xmlCreateContentVersion;
SaveResult[] createContentVersionResults = binding.create(new sObject[] { createContentVersion });

// obtain content documents
QueryResult contentDocuments = binding.query("SELECT ContentDocumentId, PublishStatus, OwnerId FROM ContentVersion WHERE Id = '" + createContentVersionResults[0].id + "'");
String contentDocumentId = contentDocuments.records[0].Any[0].FirstChild.Value;
Console.WriteLine("ContentDocumentId="+contentDocumentId);

// create content document link
XmlDocument docCreateContentDocumentLink = new XmlDocument();
XmlElement[] xmlCreateContentDocumentLink = new XmlElement[3];
xmlCreateContentDocumentLink[0] = docCreateContentDocumentLink.CreateElement("ContentDocumentId");
xmlCreateContentDocumentLink[0].InnerText = contentDocumentId;
xmlCreateContentDocumentLink[1] = docCreateContentDocumentLink.CreateElement("LinkedEntityId");
xmlCreateContentDocumentLink[1].InnerText = "006j000000SYHGu";
xmlCreateContentDocumentLink[2] = docCreateContentDocumentLink.CreateElement("ShareType");
xmlCreateContentDocumentLink[2].InnerText = "I";
sObject createContentDocumentLink = new sObject();
createContentDocumentLink.type = "ContentDocumentLink";
createContentDocumentLink.Any = xmlCreateContentDocumentLink;
SaveResult[] createContentDocumentLinkResults = binding.create(new sObject[] { createContentDocumentLink });
Console.WriteLine("ContentDocumentLinkId=" + createContentDocumentLinkResults[0].id);

Best Answer

I used the Salesforce UI to add a File to an Opportunity. Then checked the resulting ContentDocumentLink records with SQOL.

Select Id,LinkedEntityId,ContentDocumentId,IsDeleted,ShareType,Visibility from ContentDocumentLink where ContentDocumentId = '0694000000548HHAAY'

ContentDocumentLink

Note how there are two records, one to the Opportunity and one to my user record (this will be why you can't see it in your private library). The one to the Opportunity has the ShareType V (Viewer permission) and Visibility AllUsers

Related Topic