You’re using addon.getForgeAppToken(installationId) correctly, but the 401 Unauthorized you’re getting is almost certainly because the token has expired. Forge app tokens are short-lived JWTs and must be generated fresh for every request. They are not meant to be cached or reused.
What’s going wrong In your code:
let { appToken, apiBaseUrl } = await addon.getForgeAppToken(installationId);
// Later in the code...
const headers = {
Accept: 'application/json',
Authorization: `Bearer ${appToken}`
};
const apiRes = await fetch(searchUrl, { headers });
To avoid 401 Unauthorized errors, make sure you’re calling getForgeAppTokeninside the function that executes the API request, so a fresh token is generated every time.
async function fetchConfluenceContent({ installationId, spaceKeys, pageId }) {
// Generate a fresh token right before the request
const { appToken, apiBaseUrl } = await addon.getForgeAppToken(installationId);
const cql = `space in ('${spaceKeys.join("','")}') and type=page and id = ${pageId}`;
const searchUrl = `${apiBaseUrl}/wiki/rest/api/content/search?cql=${encodeURIComponent(cql)}&expand=...`;
const headers = {
Accept: 'application/json',
Authorization: `Bearer ${appToken}`
};
const apiRes = await fetch(searchUrl, { headers });
if (apiRes.status === 401) {
console.error('Token expired or invalid. Make sure you generate a fresh app token for every request.');
}
return apiRes;
}
The token will last for a few hours, I’m not sure if the time is specified anywhere but it’s certainly longer than a few seconds.
It could be to do with how your app is structured. Where is this code being called? Is it part of a Forge interaction?
getForgeAppToken in ACE accesses a cache which stores the last app token the app has “seen” for a given installation ID, and the cache gets refreshed any time the remote recieves something from Forge.
It’s possible that if you’re doing this as part of some background task for example that doesn’t involve any Forge invocation, the token in the cache has actually expired. For situations like this, you can try using a scheduled trigger to keep the token up to date.