Problems with /wiki/rest/api/search api in custom UI Forge app

Hi there,

I recently came across a strange issue with the search api in Confluence. I use it in my custom UI Confluence app.

It works fine like this:

response = await api
    .asUser()
    .requestConfluence(route`/wiki/rest/api/search?cql=${cql}&limit=50&expand=body.storage,body.dynamic,version`, {
    headers: {
     'Accept': 'application/json'
      }
 });

The cql variable has the value ‘space=DEV AND type=page’

When a query returns more than 50 pages, I want to send another request which gives me the next 50 pages. I read in the documentation that I should use the next-URL in the response. However, I realized that the expand part does not work with the next-URL as I have to use ‘expand=content.body.storage,content.body.dynamic,content.version’ instead (the results array entries have a content child element).

I tried to define the part after route as variable define the “next”-request like this

response = await api
    .asUser()
    .requestConfluence(route`${request}`, {
    headers: {
        'Accept': 'application/json'
    }
});

The request variable has the value of the “next”-url:

/wiki/rest/api/search?next=true&cursor=_sa_WyJcdDMxMDQ3OTggaUIoXz1eXkVGM1ZgYnVSaExNbmogY3AiXQ%3D%3D&expand=content.body.storage%2Ccontent.body.dynamic%2Ccontent.version&limit=50&start=50&cql=space%3DDEV+AND+type%3Dpage

When I execute this, I always get this error

{"code":401,"message":"Unauthorized; scope does not match"}

I have the search:confluence permission scope in my manifest and it is the same error for asUser() and asApp().
On the other hand, when I try it like this (without variable) it works fine and I get a 200 status and a result.

response = await api
    .asUser()
    .requestConfluence(route`/wiki/rest/api/search?next=true&cursor=_sa_WyJcdDMxMDQ3OTggaUIoXz1eXkVGM1ZgYnVSaExNbmogY3AiXQ%3D%3D&expand=content.body.storage%2Ccontent.body.dynamic%2Ccontent.version&limit=50&start=50&cql=space%3DDEV+AND+type%3Dpage`, {
    headers: {
        'Accept': 'application/json'
    }
});

Has anyone an idea what I need to do to make it work also with a variable? The 401 error doesn’t make much sense here. I already tried a lot like using request as request.toString() or JSON.stringify(request), asUser(), asApp()
nothing worked so far :frowning:
Any help is appreciated.

I solved the problem myself. It was the encoding and it finally worked after I replaced e.g. all “%3D” occurrences with “=”. So I can’t use the next-URL as it is and as it is recommended in the documentation, but have to extract the cursor value, replace all encodings and build up the new request url myself.
I hope this helps others who come across the same issue, especially when getting the error “scope does not match” although it is an issue with encoding.

1 Like

I’m seeing a similar issue with the ‘next’ URL in the Get Pages for Label API (also in Forge), except that the link returned by the first call does not have any url-encoded values:

/wiki/api/v2/labels/1671169/pages?limit=2&cursor=eyJpZCI6IjE1MDczMjkiLCJjb250ZW50T3JkZXIiOiJpZCIsImNvbnRlbnRPcmRlclZhbHVlIjoxNTA3MzI5fQ==

(Yes, 2 is a contrived limit, just to force pagination in my small dev instance.)

However, I get the same “Unauthorized; scope does not match” response.

All that said, the solution from @ij-s worked for me as well: I just extracted the cursor value from the link, and added it as a query param to the URL, and everything is fine. My code now looks like:

        response = await api
            .asUser()
            // .requestConfluence(route`${nextLink}`,
            .requestConfluence(route`/wiki/api/v2/labels/${labelId}/pages?limit=${limit}&cursor=${cursor}`,
                {
                    headers: {
                        'Accept': 'application/json'
                    }
                });

(nextLink contained the link above; cursor contains the extracted value).