title: How to POST multipart binary & non-binary with httr (for Salesforce API)
body: <p>I'm having some trouble using <em>httr</em> to POST a file to <a href="https://resources.docs.salesforce.com/204/latest/en-us/sfdc/pdf/api_rest.pdf" rel="nofollow noreferrer">Salesforce's REST API</a>. I thought <a href="https://stackoverflow.com/questions/31080363/how-to-post-multipart-related-content-with-httr-for-google-drive-api">this SO question</a> might take care of it, but didn't seem to. I have a hunch that this is because Saleforce wants a non-binary part and <code>upload_file</code> creates an object of class <code>form_file</code> that's handled as a binary, but maybe someone has an alternate explanation/solution...</p>
  
  <p>Salesforce asks for the following curl request to insert a new document:</p>
  
  <pre><code>curl https://yourInstance.salesforce.com/services/data/v23.0/sobjects/Document/ -H
  "Authorization: Bearer token" -H "Content-Type: multipart/form-data;
  boundary=\"boundary_string\"" --data-binary @newdocument.json
  </code></pre>
  
  <p>with <em>newdocument.json</em> looking like this:</p>
  
  <pre><code>--boundary_string
  Content-Disposition: form-data; name="entity_document";
  Content-Type: application/json
  {
  "Description" : "Marketing brochure for Q1 2011",
  "Keywords" : "marketing,sales,update",
  "FolderId" : "005D0000001GiU7",
  "Name" : "Marketing Brochure Q1",
  "Type" : "pdf"
  }
  --boundary_string
  Content-Type: application/pdf
  Content-Disposition: form-data; name="Body"; filename="2011Q1MktgBrochure.pdf"
  Binary data goes here.
  --boundary_string--
  </code></pre>
  
  <p>If I try to generate a similar output using @Jeroen's answer as a helpful guide, I get a Bad Request error:</p>
  
  <pre><code>library(httr)
  library(jsonlite)
  
  url &lt;- "https://[Salesforce Instance]/services/data/v39.0/sobjects/Document/"
  header &lt;- add_headers(c(Authorization = "Bearer [Salesforce Key]"))
  
  media &lt;- tempfile()
  png(media, width = 800, height = 600)
  plot(cars)
  dev.off()
  
  metadata &lt;- tempfile()
  x &lt;- data.frame(FolderId="a0a1300000ZG7u3", Name="test.png") #Salesforce Record ID and file name
  json &lt;- toJSON(unbox(x), pretty=T)
  writeLines(json, metadata)
  
  body &lt;- list(entity_document = upload_file(metadata, type = "application/json; charset=UTF-8"), 
               Body = upload_file(media, type = "image/png"))
  
  
  req &lt;- POST(url, header, verbose(), body = body)
  </code></pre>
  
  <p>(verbose output)</p>
  
  <pre><code>-&gt; POST /services/data/v39.0/sobjects/Document/ HTTP/1.1
  -&gt; Host: [Salesforce Instance]
  -&gt; User-Agent: libcurl/7.51.0 r-curl/2.3 httr/1.2.1.9000
  -&gt; Accept-Encoding: gzip, deflate
  -&gt; Accept: application/json, text/xml, application/xml, */*
  -&gt; Authorization: Bearer [Salesforce Key]
  -&gt; Content-Length: 3720
  -&gt; Expect: 100-continue
  -&gt; Content-Type: multipart/form-data; boundary=------------------------6525413b2350e313
  -&gt; 
  &lt;- HTTP/1.1 100 Continue
  &gt;&gt; --------------------------6525413b2350e313
  &gt;&gt; Content-Disposition: form-data; name="entity_document"; filename="file1510059b5200f"
  &gt;&gt; Content-Type: application/json
  &gt;&gt; 
  
  &gt;&gt; {
  &gt;&gt;     "FolderId": "a0a1300000ZG7u3",
  &gt;&gt;     "Name": "test.png"
  &gt;&gt;   }
  
  &gt;&gt; 
  &gt;&gt; --------------------------6525413b2350e313
  &gt;&gt; Content-Disposition: form-data; name="Body"; filename="file151001ac96950"
  &gt;&gt; Content-Type: image/png
  &gt;&gt; 
  
  &gt;&gt; ‰PNG
  &gt;&gt; 
  
  &gt;&gt; 
  &gt;&gt; --------------------------6525413b2350e313--
  
  &lt;- HTTP/1.1 400 Bad Request
  </code></pre>
  
  <p>with the exception of the filename in the first part, this is pretty close to the desired output, but Salesforce returns the message:</p>
  
  <pre><code>content(req)
  
  [[1]]
  [[1]]$message
  [1] "Cannot include more than one binary part"
  
  [[1]]$errorCode
  [1] "INVALID_MULTIPART_REQUEST"
  </code></pre>
  
  <p>I tried with just the json but get a slightly different Bad Request:</p>
  
  <pre><code>body &lt;- list(entity_document= json, 
               Body = upload_file(media, type = "image/png"))
  
  -&gt; Content-Type: multipart/form-data; boundary=------------------------ecbd3787f083e4b1
  -&gt; 
  &lt;- HTTP/1.1 100 Continue
  &gt;&gt; --------------------------ecbd3787f083e4b1
  &gt;&gt; Content-Disposition: form-data; name="entity_document"
  &gt;&gt; 
  &gt;&gt; {
  &gt;&gt;     "FolderId": "a0a1300000ZG7u3",
  &gt;&gt;     "Name": "test.png"
  &gt;&gt;   }
  &gt;&gt; --------------------------ecbd3787f083e4b1
  &gt;&gt; Content-Disposition: form-data; name="Body"; filename="file151001ac96950"
  &gt;&gt; Content-Type: image/png
  &gt;&gt; 
  
  &gt;&gt; ‰PNG
  &gt;&gt; 
  
  &gt;&gt; 
  &gt;&gt; --------------------------ecbd3787f083e4b1--
  
  content(req)
  
  [[1]]
  [[1]]$message
  [1] "Multipart message must include a non-binary part"
  
  [[1]]$errorCode
  [1] "INVALID_MULTIPART_REQUEST"
  </code></pre>
  
  <p>Digging into the details, it seems to me that <em>curl</em> either reads and posts the metadata file as a binary if I use <code>upload_file</code> or posts the json without the content type if I post it as character. Is this the only issue? And if so, is there any way to modify the handler to accept the content type?</p>
  
  <p>Any help is much appreciated!</p>