I'm having an issue uploading attachments via the REST API. When trying to upload attachment data (i.e. data about the attachment and the associated base64 binary data) I'm getting a 400 result (Bad Request) with a message like so:

Illegal Request
You have sent us an Illegal URL or an improperly formatted request.

My posted request body looks like this:

Content-Disposition: form-data; name="entity_attachment";
Content-type: application/json

{"ParentId":"a01J000000gmQlRIAU","Name":"Troubleshooting Request","OwnerId":"005J000000253nKIAQ"}

Content-Type: application/octet-stream
Content-Disposition: form-data; name="Body"; filename="test.txt"



Relevant header information:

Request Method:POST
Content-Type:multipart/form-data; "boundary_string_1414605653846"

I used the Salesforce Insert/Update Blob Tutorial to get to the point where I am. Note that their example only interacts with the Document type (not Attachment). However, I tried copying their Document Insert example line-by-line and I still received the 400 Bad Request error.

One other piece of information is that I am not uploading an actual file, but rather a large base64 encoded string.


2 Answers 2


You can do this using forcetk if you are planning to do this in javascript

here is sample code -

<apex:page docType="html-5.0" title="File Uploader">

Select a file to upload as a new Chatter File.

var client = new forcetk.Client();


function upload() {
    var file = $("#file")[0].files[0];
    client.createBlob('ContentVersion', {
        Origin: 'H', // 'H' for Chatter File, 'C' for Content Document
        PathOnClient: file.name
    }, file.name, 'VersionData', file, function(response){
        $("#message").html("Chatter File created: <a target=\"_blank\" href=\"/" + response.id + "\">Take a look!</a>");
    }, function(request, status, response){
        $("#message").html("Error: " + status);

here is internal forcetk implementation for sf blob [link - https://github.com/developerforce/Force.com-JavaScript-REST-Toolkit/blob/master/forcetk.js] -

    /* Low level function to create/update records with blob data
 * @param path resource path relative to /services/data
 * @param fields an object containing initial field names and values for 
 *               the record, e.g. {ContentDocumentId: "069D00000000so2", 
 *               PathOnClient: "Q1 Sales Brochure.pdf"}
 * @param filename filename for blob data; e.g. "Q1 Sales Brochure.pdf"
 * @param payloadField 'VersionData' for ContentVersion, 'Body' for Document
 * @param payload Blob, File, ArrayBuffer (Typed Array), or String payload
 * @param callback function to which response will be passed
 * @param [error=null] function to which response will be passed in case of error
 * @param retry true if we've already tried refresh token flow once
forcetk.Client.prototype.blob = function(path, fields, filename, payloadField, payload, callback, error, retry) { 
    var that = this;
    var url = (this.visualforce ? '' : this.instanceUrl) + '/services/data' + path;
    var boundary = randomString();

    var blob = new Blob([
        "--boundary_" + boundary + '\n' 
        + "Content-Disposition: form-data; name=\"entity_content\";" + "\n" 
        + "Content-Type: application/json" + "\n\n" 
        + JSON.stringify(fields) 
        + "\n\n" 
        + "--boundary_" + boundary + "\n" 
        + "Content-Type: application/octet-stream" + "\n" 
        + "Content-Disposition: form-data; name=\"" + payloadField 
          + "\"; filename=\"" + filename + "\"\n\n",
        + "--boundary_" + boundary + "--"
    ], {type : 'multipart/form-data; boundary=\"boundary_' + boundary + '\"'});

    var request = new XMLHttpRequest();
    request.open("POST", (this.proxyUrl !== null && ! this.visualforce) ? this.proxyUrl: url, this.asyncAjax);

    request.setRequestHeader('Accept', 'application/json');
    request.setRequestHeader(this.authzHeader, "Bearer " + this.sessionId);
    request.setRequestHeader('X-User-Agent', 'salesforce-toolkit-rest-javascript/' + this.apiVersion);
    if (this.proxyUrl !== null && ! this.visualforce) {
        request.setRequestHeader('SalesforceProxy-Endpoint', url);

    if (this.asyncAjax) {
        request.onreadystatechange = function() {
            // continue if the process is completed
            if (request.readyState == 4) {
                // continue only if HTTP status is good
                if (request.status >= 200 && request.status < 300) {
                    // retrieve the response
                    callback(request.response ? JSON.parse(request.response) : null);
                } else if(request.status == 401 && !retry) {
                    that.refreshAccessToken(function(oauthResponse) {
                        that.setSessionToken(oauthResponse.access_token, null,oauthResponse.instance_url);
                        that.blob(path, fields, fileName, file, callback, error, true);
                } else {
                    // return status message
                    error(request, request.statusText, request.response);


    return this.asyncAjax ? null : JSON.parse(request.response);

Content-Type is formed incorrectly in the header.

Posted value:

Content-Type:multipart/form-data; "boundary_string_1414605653846"

Should be:

Content-Type:multipart/form-data; boundary=boundary_string_1414605653846

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.