How to POST attachment to Confluence page using REST API with perl?

I am trying to send file1.jpg to Confluence as an attachment to a page with ID 44073843, via perl's REST::Client module, using an existing, known good, $client connection that has already been successfully used to perform a GET of that page, immediately before this POST request.

The code I'm using to do that is:

$headers = {
    Accept => 'application/json',
    Authorization => 'Basic ' . encode_base64($user . ':' . $password),
    'X-Atlassian-Token' => 'no-check',
    'Content-Type' => 'form-data',
    Content =>
        [ file => ["file1.jpg"], ]};
$r = $client->POST('rest/api/content/44073843/child/attachment', $headers);

$client->responseCode() after execution of the POST is 500.

This was my best attempt at replicating in perl, the following curl example cited in Atlassian documentation:

curl -v -S -u admin:admin -X POST -H "X-Atlassian-Token: no-check" -F "file=@myfile.txt" -F "comment=this is my file" "http://localhost:8080/confluence/rest/api/content/3604482/child/attachment" | python -mjson.tool

The above curl command, when run with 'file1.jpg' in place of 'myfile.txt', and the appropriate URI for our local installation, does indeed upload attachments to the specified page.  There's just something in how I'm trying to use perl to create a multi-part form post that's not working, but I don't know what it is.  Is curl magically inserting some header for me that perl doesn't insert unless I explicitly add it to the headers list? Is my Content-Type or Content specification not what Confluence is expecting? I removed the comment field, as the API docs say that it's optional.  And, of course, I suspect that what I've done above doesn't actually include the file's contents, just its name, so I want to know how to send the file's contents, as well.  Can I just slurp the file into a string and pass that string?  Do I have to specify a header that says that I'm sending binary data?  I've never had to deal with a multipart form before, so I'm not sure that I'm doing it correctly.

Note:  I also tried a content type of 'multipart/form-data', and got the same results.

As a last ditch effort, I wanted to try to replicate what happens when I interactively upload an attachment, but in Confluence 5.8 (the version we are using), the upload attachment functionality appears to use the old prototype REST API, so merely creating a message that looks like the one that sends, isn't what I want to be doing.

After 24 hours, my post to stackoverflow hasn't gotten any replies, either.

Any thoughts?

1 answer

1 accepted

1 vote
Answer accepted

I was doing something simliar in perl,  here was my code that worked:


$response = $self->browser()->request( POST $self->confluenceUrl."/rest/api/content/$pageId/child/attachment",
                                             'Content_Type' => 'form-data',
                                             'Content'      => [ 'file' => [$attachment] ],
                                             'X-Atlassian-Token' => 'no-check',
                                             'Authorization' =>  'Basic ' . $self->credentials );

It does look like I used "Content_Type" rather than "Content-Type" as the key.

Scott, this looks interesting, and if you got it to work, I'm very curious! Were you using REST::Client, or some other package? Not sure I recognize it...

Hi, So, my approach was to create a "Confluence" object/module where I wrapped all of the syntax needed. The modules I'm using to make it all work are JSON, MIME::Base64, Rest::Client, LWP::UserAgent, HTTP::Request::Common. At the time, I was porting a TWiki web into Confluence. I was also using a custom rest interface to set the author and creation date as the default REST interface did not allow that. If you want more details send me an email at and I'll share the code with you.

Hi Helen,  thanks for your private email.  I decided perhaps the best way to get started was to post the full example code to BitBucket, so I did:

Take a look at the code there and see if it helps you to get over your hurdle.  One of the things that I did was use Rest::Client to interact with Confluence for everything except uploading attachments.  For uploading attachments, I dropped back to LWP.


Scott, thanks for the bigger picture.

It turns out that I didn't have my headers array structured properly for the LWP POST call – resulting in it not getting the proper content type to know it was a multi-part form, resulting in the server rejecting the request with a 415 error.  The moral of the story is that if you're getting a 415 error, you really ARE doing something wrong with your Content-Type, even if you're certain that you're not.

If you're having trouble getting this to work, definitely have a look at the code Scott posted.

Suggest an answer

Log in or Sign up to answer
Community showcase
Published yesterday in Agile

Managing Projects in Several Jiras at the Same Time

Have you ever noticed that fixing specific problems might be a door opener for a bigger challenge, affecting a wider audience? This was exactly the case when we, as a service company working with mul...

92 views 1 12
Read article

Atlassian User Groups

Connect with like-minded Atlassian users at free events near you!

Find a group

Connect with like-minded Atlassian users at free events near you!

Find my local user group

Unfortunately there are no AUG chapters near you at the moment.

Start an AUG

You're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs

Groups near you