How do I make Jira REST API calls from Confluence Add-on?

I know that Confluence add-ons don’t natively support this… because cross-product validation isn’t supported

I see people suggest using basic authentication between a Jira add-on that makes the API calls for you. How do I even make a API request in a Jira add-on within routes/index.js. I can only use AP.request() in a .hbs file so I either need to pass that information back to the route that renders it or find another method that natively support the JWT built-in authentication. Sorry if this is a dumb/easy question. All of my experience is data-science related and my internship has me on this side project…

1 Like

If you’re making API calls to Jira from a Confluence app you won’t use JWT to authenticate your request as it’ll fail as the JWT is created for Confluence.

Since you’re using basic auth you can use any http request method. In the frontend of HBS file template you can use straight JavaScript or jQuery (ajax) to make the call. You’d just need to pass the username:password in the request.

One thing you might want to consider is using a 3 legged OAuth method to authenticate with Jira. Then from Confluence you can have your user authenticate with a click of a button and they don’t need to provide their credentials to authenticate. Once authenticated then you can make REST API calls to JIRA.

See for more information on 3LO


Hey, I seem to have trouble making the Confluence macro in the .HBS file actually make the request. I’m trying to return all the issues from the REST JIRA API JQL Search url. When I enter the link when I’m logged in browser it returns the appropriate JSON. When I test out the API token with curl as seen here it returns the appropriate JSON. But when I attempt to do it in the .HBS file in the macro with fetch(), the macro sends out an OPTIONS request instead of a GET request and has no header (which contains all the authentication) which results in no response. Why is that? Below is the code contained within the <script> tags…

let url = ''

        let h = new Headers();
        h.append('Accept', 'application/json');

        let encoded = window.btoa(' TOKEN');
        let auth = "Basic " + encoded;
        h.append('Authorization', auth)

        let req = new Request(url, {
            method: 'GET',
            headers: h

        .then( (res) => {
            return res.json().then( (data) => {
                if(res.ok) {
                    return data;
                } else {
                    return Promise.reject({status: res.status, data});
        .then((data) => {

I’m just letting you know that this doesn’t work… More details about the solution you told me about it in a flagged comment on this post.

Hmm…sorry I missed your note before. Looking at this documentation on OPTIONS requests I see that it’s trying to be helpful in CORS requests which makes me think that there is something the all.js file is doing to all requests.

I put in a question with the JS team to see if they can help here.

Hi @EmekaMbazor,
Please make sure you don’t put any private tokens in code that runs in the browser. I think that 3LO calls to may be the best way forward since the invocations will be made under the current user’s consent and there won’t be any CORS issues. Our front end request API will prevent cross product calls also.

1 Like