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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .