How to get uploaded image url using the Discourse API?


(Jakob Borg) #11

Oh, I know what curl is. I’m just pointing out that I don’t see where you’re sending the actual contents of the file you are supposedly uploading to Discourse. Without contents I don’t expect Discourse to be happy with the upload, so I wouldn’t expect a successful result.

(sleep) #12

Get rid of the function http_build_query, we can upload image successfully!

public function upload_file($user) {
  $url = 'http://hostname/uploads?api_key=api_key&api_username=api_username';
  $poststr = http_build_query(array('username' => $user, 'type' => 'image', 'file' => '/uploads/file/filename', 'synchronous' => 'true'));    // get rid of the function http_build_query, we can upload image successfully!
  $data = _curl($url, $poststr));  // _curl() : a custom function 
  return $data;

After change

  $poststr = array('username' => $user, 'type' => 'image', 'file' => '/uploads/file/filename', 'synchronous' => 'true');    // get rid of the function http_build_query, we can upload image successfully!

In summary, when we submit data to discourse server using curl, we may use three method to post parameters. You should try more methods when you encounter problems.

one: json
two: http_query_query
three: array

There may be more.:slightly_smiling:

(Stefan Milenkovic) #13

hi all,

i am trying to upload image using ajax, and getting 400 bad request.
but there is no message so i can see what is going on.
Have my csrf token, and also logged in but still nothing.

code looks something like this

                    headers: { 'X-CSRF-Token': data.csrf },
                    url: '/forum/uploads.json?synchronous=1',
                    type: 'POST',
                    data: formData,
                    cache: false,
                    contentType: false,
                    processData: false,
                    success: function (xhr) {

i guess it is maybe because i am using formData, if it is can you please tell me in which format i need to send image?


(Blake Erickson) #14

I’m not really sure what all you will need to do to get it to work via ajax (api key, csrf, cors, etc?), but here is how you can get an upload to work via the api (this is for an avatar upload, but it is the same endpoint, you probably just need to change the type):

(Stefan Milenkovic) #15

i actually want to first upload image, and than with that image to make a post.
i made posts without api_key and api_username since i have csrf token and cookie,
so i guess i don’t need for this upload too.

this is my call for creating post :

                headers: { 'X-CSRF-Token': data.csrf },
                data: JSON.stringify({
                    raw: this.get('comment'),
                    topic_id: this.get('topicId')
                contentType: 'application/json',

so as you can see, without api_key and api_username.

but can you tell me more about this type, what should i put if i want to upload image before post?
and user_id, i don’t have user id, and i didn’t use it for any of the for any calls until now.

so only cookie and csrf

(Blake Erickson) #16

sorry the user_id is only if the type is avatar.

I’m pretty sure you will need a post_id though if you are uploading it to a post.

I’m not sure off the top of my head, but you can:

The current issue you will run into though is that we don’t return the upload_id:

(Stefan Milenkovic) #17

exactly, that’s where i am stuck right now :slight_smile:

so, i made a call, uploaded picture, but there is no url to the picture in the response, only success : OK.
is there a way to get url to the image in the response?


(Stefan Milenkovic) #18

i saw here: Using the Discourse API to post with uploaded files?
that there is a way to get the link in response,
but i am using this synchronous param set to 1 and still nothing but success : OK

(Stefan Milenkovic) #19

this was more a question. So is there a clean a way, proposed by discourse to upload image via API, and retrieve image in response?

Or maybe to make upload and then request again for image link?


(Blake Erickson) #20

One of the issues you are having has to do with the type parameter being set to ‘POST’ in all caps. It needs to be a value that complies with this rule:

Besides being used for determining if you are uploading an “avatar”, I’m not totally sure yet what all type is used for. So I would just set it to some all lowercase string like “upload”.

Here is an example file upload request and response API call:

(Stefan Milenkovic) #21

i did everything u said :

    formData.append('type', 'upload');
    formData.append('post_id', '6640');
    formData.append('files', imageFile);
    formData.append('synchronous', true);

                headers: { 'X-CSRF-Token': data.csrf },
                url: '/forum/uploads',
                type: 'post',
                data: formData,
                async: false,
                cache: false,
                contentType: false,
                processData: false,
                success: function (status, xhr, msg) {
                    if (status) {

                error: function (status) {
                    if (status) {

and still nothing but success : OK.

do you have maybe any other idea, what it can be?

(Michael - #22

You can only do a synchronous upload as a staff user, see Image upload with synchronous true for new users for details.

Some more information

if you want the url back, you will have to listen to the “/uploads/*” channels via the Message Bus.

(Blake Erickson) #23

I’m pretty sure since you aren’t using an API key you will have to be a staff user. Is your user a staff user?

(Stefan Milenkovic) #24

no i am not, but i can be :slight_smile:
i will do my posts with stuff user, and problem solved.

but still, if the image is uploaded even without stuff user, what’s the difference?
i am able to upload it, but just can’t see the url.

thanks a lot guys.

(Blake Erickson) #25

If you are a staff user and you do syncrounous it should return the image url and id

(Stefan Milenkovic) #26

i did a call with api_key and username and i easily get url to the image.
but unfortunately i don’t have api_key in all the cases, so i needed to fall back to message-bus.

can you explain me how to get url in the message-bus/poll request
i did almost everything

with the same client_id, i made upload of image and got success : ok
i’ve sent ‘/uploads/composer’:0, ‘/uploads/composer’:{current_message_id}, again no luck

i’ve seen also in the code, inside upload controller
something like this MessageBus.publish("/uploads/#{type}", data.as_json, client_ids: [client_id])

i tried also with type, again no luck.

can you just explain what params i need to send for message-bus/poll
so i can get url of image in the response?

thanks a lot!

Uploading files (async) and get url back?
(DenisD) #27

Is there any complete answer found to get upload URL with ajax?
We are also interested to get one.
many thanks.

(DenisD) #28

Sorry for recalling rather old topic but we appreciate any support on getting uploaded url for non-staff user right after upload. many thanks.

(Blake Erickson) #29

Here is a complete ajax file upload example which I verified works with non-staff users:

<!DOCTYPE html>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
    <meta content="utf-8" http-equiv="encoding">
      var api_username = '788336e9f81d21b4fa04';
      var api_key = '8f23cba0eec7cff2a995a8271589f5096d9b448d29af5d2e93383e43ac6e0f9b';
      function createPostWithUpload(url, width, height) {
        var data = {
          topic_id: "18",
          raw: '<img src=' + url + ' width=' + width + ' height=' + height + '>',
          api_username: api_username,
          api_key: api_key
          type: "POST",
          url: "",
          data: data,
          success: false,
          dataType: false
      function uploadFile() {
        var form = $('#uploadForm')[0];
        var formData = new FormData(form);
        formData.append('api_username', api_username);
        formData.append('api_key', api_key);
        formData.append('synchronous', 'true');
        formData.append('type', 'composer');
        var data = {
          'files[]': formData,
          type: "composer",
          synchronous: true,
          api_username: api_username,
          api_key: api_key
          method: "POST",
          url: "",
          data: formData,
          processData: false,
          contentType: false,
          success: function(response) {
            var id =;
            var url = response.url;
            var width = response.width;
            var height = response.height;
            createPostWithUpload(url, width, height);
      function createPost() {
        var data = {
          topic_id: "18",
          raw: "my sample post that I'm making",
          api_username: api_username,
          api_key: api_key
          type: "POST",
          url: "",
          data: data,
          success: false,
          dataType: false
    <form name="uploadForm" id="uploadForm">
      <p><input id="myFile" type="file" name="file"> </p>
      <p><input type="button" onClick="uploadFile();" value="Upload file"></p>
      <input type="button" onClick="createPost();" value="Create Post"></p>

(Michael - #30

That code is seriously too bloated, with a formData structure inside an almost identical data structure.

But more important: there is no non-staff synchronous upload issue any more, the code was refactored last week.