Come for the products,
stay for the community

The Atlassian Community can help you and your team get more value out of Atlassian products and practices.

Atlassian Community about banner
Community Members
Community Events
Community Groups

How do I upload attachment to JIRA Issue via REST API?

Is it possible to upload attachment in java using REST API without using cURL ?

Is it possible to upload a file using REST API BROWSER ?

11 answers

1 accepted

7 votes
Answer accepted

Here my code for upload and download attachment :

public boolean addAttachmentToIssue(String issueKey, String fullfilename) throws IOException{

		CloseableHttpClient httpclient = HttpClients.createDefault();
		HttpPost httppost = new HttpPost(jira_attachment_baseURL+"/api/latest/issue/"+issueKey+"/attachments");
		httppost.setHeader("X-Atlassian-Token", "nocheck");
		httppost.setHeader("Authorization", "Basic "+jira_attachment_authentication);
		File fileToUpload = new File(fullfilename);
		FileBody fileBody = new FileBody(fileToUpload);
		HttpEntity entity = MultipartEntityBuilder.create()
				.addPart("file", fileBody)
        String mess = "executing request " + httppost.getRequestLine();;
        CloseableHttpResponse response;
        try {
			response = httpclient.execute(httppost);
		} finally {
		if(response.getStatusLine().getStatusCode() == 200)
			return true;
			return false;

	public boolean getAttachmentFromIssue(String contentURI, String fullfilename) throws IOException {
		CloseableHttpClient httpclient = HttpClients.createDefault();
        try {
            HttpGet httpget = new HttpGet(contentURI);
            httpget.setHeader("Authorization", "Basic "+jira_attachment_authentication);
            System.out.println("executing request " + httpget.getURI());

            CloseableHttpResponse response = httpclient.execute(httpget);
            int status = response.getStatusLine().getStatusCode();
            if (status >=200 && status < 300) {
            	HttpEntity entity = response.getEntity();
            	if (entity.isStreaming()) {
            		byte data[] = EntityUtils.toByteArray(entity);
            		FileOutputStream fout = new FileOutputStream(new File(fullfilename));
		} finally {
        return true;

jira_attachment_authentication is define elsewhere as :

jira_attachment_authentication = new String(org.apache.commons.codec.binary.Base64.encodeBase64((user+":"+pass).getBytes()));

Can you show me your which is getting the file? File fileToUpload = new File(fullfilename); FileBody fileBody = new FileBody(fileToUpload);

Loïc GRIVEAU, if I try to use the same code to move attachments from one JIRA instance to another, it gives 415 error. : 415 Unsupported Media Type.


Any idea?

Like # people like this

Hi, can you pls share the jar/lib that you used in your program?

you save my day, thanks!!

I managed to make it work with PostMan.

Header must contain:

Content-Type: multipart/form-data

X-Atlassian-Token: no-check 


Body must be of type "form-data" and must contain a key named "file" with value a specified file (I had to use the file picker, see the picture).


Thanks, this worked for me.

Hi @Danut M _StonikByte_  , @Ramandeep Singh Nagi ,


I tried to attach the attachment via postman by using your above Info. When I used Content-Type as "multipart/form-data" am getting the below error.


But I removed Content-Type and my postman was successful.



Did I missed anything with "Content-Type" ?




Hello Parthiban,


I used the following settings in my Postman:




Content-Type: multipart/form-data,

X-Atlassian-Token: no-check,

charset: UTF-8,

Accept-Encoding: gzip, deflate, br

Body -> form-data:

file: <give path to your file to be attached>


I hope this helps.



Ramandeep Singh

Thanks! Worked like a charm.

@Ramandeep Singh Nagi Could you kindly post a screenshot of your solution?

This works for me:


  const auth = 'Basic ' + new Buffer(props['jiraAppUserName'] + ':' + props['jiraAppPassword']).toString('base64');
var options = {
url: 'https://JIRAUAT.xx.xx/rest/api/latest/issue/XEA-1255/attachments',
headers: {
'Authorization': auth,
'X-Atlassian-Token': 'nocheck'

var r =, function (err, res, body) {
if (err) {
messages: 'outch',
obj: {}
} else {
console.log('Upload successful! Server responded with:', body);
messages: 'successfully updated jira ticket',
obj: {}

var form = r.form();
form.append('file', fs.createReadStream('./somefile.png'));


Can you please attach the complete file/code?

Like Ian Keller likes this
HttpRequestFactory requestFactory = new NetHttpTransport().createRequestFactory(parameters);

MultipartContent content = new MultipartContent().setMediaType(
new HttpMediaType("multipart/form-data").setParameter("boundary", "__END_OF_PART__"));

for(Attachment attachment : attachments) {
File attachmentFile = ComponentAccessor.getAttachmentManager().
streamAttachmentContent(attachment, new FileInputStreamConsumer(attachment.getFilename()));

String mimeType = URLConnection.guessContentTypeFromName(attachmentFile.getName());

FileContent fileContent = new FileContent(mimeType, attachmentFile);
MultipartContent.Part part = new MultipartContent.Part(fileContent);
part.setHeaders(new HttpHeaders().set(
String.format("form-data; name=\"file\"; filename=\"%s\"", attachmentFile.getName())));

HttpRequest request = requestFactory.buildPostRequest(jiraUrl, content);
request.getHeaders().set("X-Atlassian-Token", "no-check");
return request.execute();

Java code using google-http to POST attachments to /rest/api/2/issue/{issueIdOrKey}/attachments using OAuth. Still trying to figure out format for posting attachments on issue create.

What will be body for this endpoint i am using postman and it is not working for me : /rest/api/2/issue/{issueIdOrKey}/attachments

can anyone share postman body.

neither for me.... i have try all :(

Hi guys ! I've managed to POST attachment via Postman. An example :
Everything works fine.

However, I need to implement via Java service which use OAuth1.
First, there is a need to manipulate header, that is, to insert

"X-Atlassian-Token", "no-check"

and then, to upload actual input streams.

Any ideas ?

My example has it shown 

request.getHeaders().set("X-Atlassian-Token", "no-check");

What is the problem? 

Hi ! Thanks for a quick response !
Well, I've done authentication via OAuth, from a Jira tutorial, and have this method for post request, and i don't know how to manipulate header :

public HttpResponse executePrimitivePostRequest(@NonNull String url, @NonNull GenericData contentGenericData) {
String apiCallUrlPath = JIRA_HOME_URL + url;

try {
OAuthParameters parameters = jiraOAuthClient.getParameters( JIRA_ACCESS_TOKEN, JIRA_SECRET_KEY, JIRA_CONSUMER_KEY, JIRA_PRIVATE_KEY );
HttpContent content = new JsonHttpContent( new JacksonFactory(), contentGenericData );
HttpResponse response = postResponseFromUrl( parameters, new GenericUrl( apiCallUrlPath ), content );

return response;
} catch (HttpResponseException hre) {
String errMsg = "Executing Post Request Error. " + hre;
LOGGER.log( Level.SEVERE, errMsg, hre );
throw new RuntimeException( errMsg, hre );
} catch (Exception e) {
String errMsg = "Executing Get Request, no result.";
LOGGER.log( Level.INFO, errMsg, e );
throw new RuntimeException( errMsg, e );

Yes it's possible. For example JIRA REST Java Client does that:

You'll have to send multipart request - the code will depend on HTTP client that you use.

There is already answer for that question here:

I copy paste the answer from the stackoverflow.comlink, but some dependencies are missing (MultipartEntity, HttpEntity, ...)

Can you help me ?

you can download dependencies from here

@Loïc GRIVEAU, are you able to attach to issues using sample in stackoverflow link?

if yes then can you sahre your code here?

@Aleksander Mierzwicki, is there a way we can copy ALL attachments from one JIRA issue in one server to another JIRA issue in another server? I have been trying all the code snippets given for this question and am getting 415 Unsupported media type error.

I've done the other way, by pure "spring-like" flow so to speak.
Actually, I have a complete git project, however, for the attachment, I still need to add functionality, however, I'll give you fast and dirty solution until I add in project.

For details please visit :

Here is :


ResponseEntity<ResponseIssueCreationDto> createIssueWithAttachmentStreamForSupport(
@FormDataParam("summary") String summary,
@FormDataParam("nmeTeam") @NonNull String nmeTeam,
@FormDataParam("handOverDate") @NonNull Date handOverDate,
@FormDataParam("supportType") @NonNull String supportType,
@FormDataParam("description") String description,
@FormDataParam("siteAddress") String siteAddress,
@FormDataParam("siteContacts") String siteContacts,
@FormDataParam("topology") String topology,

@FormDataParam("file") InputStream attachment,
@FormDataParam("file") FormDataContentDisposition fileDetail,
@FormDataParam("file") FormDataBodyPart body);



REST implementation :

public ResponseEntity<ResponseIssueCreationDto> createIssueWithAttachmentStreamForSupport(
String summary, String nmeTeam,
Date handOverDate, String supportType, String description,
String siteAddress, String siteContacts, String topology,
InputStream attachment,
FormDataContentDisposition fileDetail,
FormDataBodyPart body) {

return ResponseEntity.status( HttpStatus.OK )
.contentType( APPLICATION_JSON )
supportType, nmeTeam, handOverDate, siteAddress,
siteContacts, topology, description,
summary, attachment, fileDetail, body ) );


Custom service (interface) :

ResponseIssueCreationDto createIssueWithStreamAttachmentForSupport(@NonNull String supportType, @NonNull String nmeTeam,
Date handOverDate, String siteAddress,
String siteContacts, String topology,
String description, String summary,
InputStream attachment,
FormDataContentDisposition fileDetail,
FormDataBodyPart body);

Custom service implementation :

public ResponseIssueCreationDto createIssueWithStreamAttachmentForSupport(String supportType, String nmeTeam,
Date handOverDate, String siteAddress,
String siteContacts, String topology,
String description, String summary,
InputStream attachment,
FormDataContentDisposition fileDetail,
FormDataBodyPart body) {

ResponseIssueCreationDto newIssue = createBasicIssueWithoutAttachmentForSupport(
supportType, nmeTeam, handOverDate, siteAddress, siteContacts, topology, description, summary );

addAttachmentToIssue( attachment, fileDetail, body, newNocIssue.getIssueKey() );

return newNocIssue;


public ResponseIssueCreationDto createBasicIssueWithoutAttachmentForSupport(
String supportType, String nmeTeam, Date handOverDate, String siteAddress,
String siteContacts, String topology, String description, String summary) {

//<editor-fold desc="Stylized error message, for only missing mandatory fields">
boolean isErrSupportType = supportType == null;
boolean isErrNmeTeam = !NocNMETeam.isValidTeam( nmeTeam );
boolean isErrHandOverDate = handOverDate == null;
String errNew = (isErrSupportType ? "Support type, " : "")
+ (isErrNmeTeam ? "NME team, " : "")
+ (isErrHandOverDate ? "Handover date " : "");
String errorMessage = errNew.substring( 0, ((isErrSupportType || isErrNmeTeam) && !isErrHandOverDate) ?
errNew.length() - 2 : errNew.length() ).concat( " must be defined." );

Commons.ensureNonNullsWithMessageFor( errorMessage, supportType, nmeTeam, handOverDate );

//<editor-fold desc="Service desk id, and Request type id">
ServiceDesksDto serviceDeskProjectDto = getAllServiceDesks().orElseThrow( () -> new JiraAllServiceDesksException( errorMessage ) );

ServiceDeskProjectDto sdp = serviceDeskProjectDto
.filter( p -> JiraNocProjectKey.equalsIgnoreCase( p.getProjectKey() ) )
.orElseThrow( () -> new JiraServiceDeskProjectException( errorMessage ) );
String projectIdStr = Optional.ofNullable( sdp.getId() ).orElseThrow( RuntimeException::new );

ServiceDesksRequestTypeDto serviceDesksReqTypeDto = getServiceDeskWithRequestTypeForDesk( Long.valueOf( projectIdStr ) )
.orElseThrow( () -> new JiraServiceDeskRequestTypeException( errorMessage ) );
ServiceDeskRequestTypeValueItemDto srvDesksReqTypeValDto = serviceDesksReqTypeDto
.filter( f -> projectIdStr.equals( f.getServiceDeskId() ) )
.filter( n -> JiraNocSupport.equalsIgnoreCase( n.getName() ) )
.orElseThrow( () -> new JiraServiceDeskRequestTypeValueException( errorMessage ) );

GenericData rootNodeResult = new GenericData();
rootNodeResult.put( ServiceDeskId, projectIdStr );
rootNodeResult.put( RequestTypeId, srvDesksReqTypeValDto.getId() );

GenericData childrenRequestFieldValues = new GenericData();

Date hoDate3BDays = DateUtil.getEndBusinessDayFor( handOverDate, 3 );
String hoDate = new SimpleDateFormat( JIRA_MISTERY_DATETIME_PATTERN2 ).format( hoDate3BDays );
childrenRequestFieldValues.put( REQIRED_BY_DATE, hoDate );

childrenRequestFieldValues.put( SITE_ADDRESS, Commons.getFieldWithValidLengthOf255( siteAddress ) );
childrenRequestFieldValues.put( SITE_CONTACTS, siteContacts );
childrenRequestFieldValues.put( TOPOLOGY, topology );
childrenRequestFieldValues.put( SUMMARY, summary );
childrenRequestFieldValues.put( DESCRIPTION, description );

GenericData childReqNMETeam = new GenericData();
try {
childReqNMETeam.put( "value", NocNMETeam.getValidCode( nmeTeam ) );
childrenRequestFieldValues.put( NME_TEAM, childReqNMETeam );
} catch (UnsupportedOperationException nte) {
throw new JiraNOCTeamException( NocNMETeam.getValidTeams(), JIRA_HOME_URL, JiraNocReqCustPortalId, JiraNocReqCustomerPortalCreateId );

GenericData childReqSupportType = new GenericData();

try {
childReqSupportType.put( "value", NocSupportType.getValidCodeFor( supportType ) );
childrenRequestFieldValues.put( SUPPORT_TYPE, childReqSupportType );
} catch (UnsupportedOperationException nst) {
throw new JiraNOCSupportTypeException( NocSupportType.getValidTypes(), JIRA_HOME_URL, JiraNocReqCustPortalId, JiraNocReqCustomerPortalCreateId );

rootNodeResult.put( RequestFieldValues, childrenRequestFieldValues );

return jiraClient.executeBasicPost( ResponseIssueCreationDto.class, apiCallIssueCreateByRequestType, rootNodeResult, true );


This is what you actually need :

public List<ResponseItemAttachmentDto> addAttachmentToIssue(InputStream inputStream, FormDataContentDisposition fileDetail,
FormDataBodyPart body, @NonNull String issueKey) {

String urlParamCall = MessageFormat.format( jiraAddAttachmentToIssueKey, issueKey );

GenericData rootNodeResult = new GenericData();
rootNodeResult.put( ATTACHMENT, new AttachmentInfoHolderDto( inputStream, fileDetail, body ) );

GenericData headers = new GenericData();
headers.put( "X-Atlassian-Token", "no-check" );

return jiraClient.executePostExpectingList( ResponseItemAttachmentDto.class, urlParamCall,
headers, rootNodeResult, true );

Hello, just facing problem with UTF-8 characters in file name.
When uploading with postman, like in example everything is fine, but when uploading by java API, german umalut characters are converted as ???? 
Any suggestion?

Here's a working method from a Python 3.x module. The key things that aren't spelled out in many solutions:

  • all file data must be read as binary data, even plain text files
  • the multipart boundary string need not be anything fancy, but it must be a string that is unlikely to occur in the file data
  • multipart lines are separated by Carriage Return + Line Feed
  • all the multipart strings must be converted to binary, then wrapped around the binary file contents
  • when calculating "Content-Length", take the length of the entire multipart form, not just the length of the file contents
def add_attachment(self, path):
    '''Upload a file attachment from a local file path.
    error_messages = []
        f = open(path, 'rb'# all files, even plain text, must be read as binary data
        file_contents =
        error_messages.append('cannot read ' + path)

    if not error_messages:
        # create multipart/form-data as defined in RFC 1867 (see:
        boundary = binascii.hexlify(os.urandom(16)).decode('ascii')
        separator = '\r\n' # CRLF is the standard separator in multipart/form-data
        # multipart/form-data consists of: prefix + file contents + suffix
        prefix = '--{}{}'.format(boundary, separator)
        prefix += 'Content-Disposition: form-data; name="file"; filename="{}"{}'.format(os.path.basename(path), separator)
        prefix += 'Content-Type: application/octet-stream{}'.format(separator)
        prefix += separator
        suffix = separator + '--{}--{}'.format(boundary, separator)

        # convert the prefix and suffix text to binary, and then join them around the binary file contents
        prefix = prefix.encode('utf-8')
        suffix = suffix.encode('utf-8')
        data = b''.join([prefix, file_contents, suffix])

        headers = {}
        headers['Content-Type'] = 'multipart/form-data; boundary={}'.format(boundary)
        headers['X-Atlassian-Token'] = 'no-check'
        headers['Content-Length'] = str(len(data)) # length must include the entire multipart/form-data, not just the file contents

        api = 'api/2/issue/' + self.key + '/attachments'
        (status, response) = self._session._call_api(api, headers=headers, data=data)
        if not (status >= 200 and status <= 299):
            error_messages += standard_errors(status, response)

    return error_messages



Do you have sample code for downloading all attachments of all Jira issues reported in a Jira project? 

Thank you;


HttpPost postRequest = new HttpPost("URL+Post REST API");

does this mean like this

HttpPost postRequest = new HttpPost("https:/");



public class JiraRest {

public static void main(String[] args) throws ClientProtocolException, IOException
String pathname= "<Full path name of the attachment file>";
File fileUpload = new File(pathname);

HttpClient httpClient = HttpClientBuilder.create().build();
HttpPost postRequest = new HttpPost("URL+Post REST API");
BASE64Encoder base=new BASE64Encoder();
String encoding = base.encode ("username:password".getBytes());
postRequest.setHeader("Authorization", "Basic " + encoding);

MultipartEntityBuilder entity=MultipartEntityBuilder.create();
entity.addPart("file", new FileBody(fileUpload));
HttpResponse response = httpClient.execute(postRequest);

Required JARs:

All JARs in lib folder of httpcomponents-client-4.5-bin and sun.misc.BASE64Decoder.jar

Awesome. This worked perfectly..

Where is the ticket URL


Sorry I posted this answer more than 3 years back, I don't remember anything as of now 

if you want to do it through perl then try with this

Suggest an answer

Log in or Sign up to answer