Cannot download cloud recording with oauth token - Forbidden 124

I’m getting this error when trying to download a recording file using an oauth token.
{"status":false,"errorCode":124,"errorMessage":"Forbidden"}

Command I used:
curl -H "Authorization: Bearer ACCESS_TOKEN" https://dropbox.zoom.us/rec/download/A6Uxc...

Tried this and got the same error:
curl https://dropbox.zoom.us/rec/download/A6Uxc...?access_token=ACCESS_TOKEN

What do I need to do differently here? Thanks in advance!

@choksheak-dbx are you appending the access token to your request?

Yes I think so. Already provided the 2 different curl commands I tried.

@choksheak-dbx thanks for the info. here is another reason, why this could occur,
if you are using a server to server Oauth token from user A and they dont have permission to view / download recordings from user B.

I would also check by pasting the link in your browser if the access token is being correctly appended.

Thanks

Hi Ojus, the oauth token is for my Business account login, and I’m trying to download the recordings for my own account for my own meetings. What are some (other) things I can try out now? Are you sure downloading cloud recording files work for other people? Thanks.

@choksheak-dbx please check dm

Debugged over zoom and turns out that the access token expired after 1 hour! After using a new access token, it works fine. Thanks for your help!

@ojus.zoom I’m having this issue as well, though I’m trying to download with the download_url provided by my zoom app’s recording webhook. As the recording is of a test transcript with no sensitive info I will share data other than my accessToken. Here is my code so far:

// the download url only has one slash after https: so that the text of the full link is shown
const downloadUrl =
‘https:/us06web.zoom.us/rec/webhook_download/5pQOFgo11lh8bdD5D_JXySBAThsijV5e7HyzDOk1-7yiyb61SWYoL-rXlb1b6oyX3H5dRhB5Rz4I4iIl.7GmPHEMwA26AyXVC/8TnUnuaxY8jClO1M04BSrJ1a21s9fjxSIXCTmTLvHdYDAxTyRBmO7Lpxp_M6hvXN.lc-MMUQTU3j_1rjL?type=cc’;
const accessToken =
‘eyJzd…’;
const passcode = ‘*8n55bh3’;
const download_token =
‘eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJodHRwczovL2V2ZW50Lnpvb20udXMiLCJhY2NvdW50SWQiOiJXbDJhaWVoZVJXU2VrZzQ1SEJ6X0RRIiwiYXVkIjoiaHR0cHM6Ly9vYXV0aC56b29tLnVzIiwibWlkIjoiNzlDc3lWQ2dRWUtlTmFTK2dBRHJ5dz09IiwiZXhwIjoxNjkzNTg3NTIyLCJ1c2VySWQiOiJna09mcmNWS1J3eTNVOFVMaFlxclNBIn0.fC5pNDVY8xif054KXD2DMQLLl6viI4AHbA-JZZoJ72nWCBuN9xzlOJPQct6uNe1YlEF40H80I2-KF_NTnyhZ5w’;
const newUrl = ${downloadUrl}?password=${passcode};
try {
const response = await axios.get(newUrl, {
headers: {
Authorization: Bearer ${accessToken},
},
});

	const vttContent = response.data;
	console.log('VTT file content:', vttContent);

I have tried sending the request to a modified url that includes the vtt file download’s passcode, and tried sending it to the unmodified downloadUrl provided by zoom.

My access token is not expired at the time of my latest tests, as of sending this message.

My recording permissions for my app are as follows:

I’m sure there’s something obvious that I’m missing here? I’ve tried using the access token in my header and the download_token as well.

Thank you for your help!

@chunsiong.zoom hi Chunsiong! Hope you’re having a great day :slight_smile: I just saw that you’re active in the forums and wanted to tag you for visibility on this issue. Perhaps Ojus is busy with other things at the moment. Can you give any guidance on how I can solve this issue?

Thank you for your help!

@nextlvlai whats the error that you are getting? Can you also check for the cases mentioned here: https://support.zoom.us/hc/en-us/articles/4414085489165-Changing-cloud-recording-chat-transcript-visibility ?

Hi @ojus.zoom ! Thanks for the reply :slight_smile:

We already have visibility turned on in those settings. The error is a 401 forbidden error that I’m getting. Are there additional settings I should check other than those listed above and here? Happy to give further info tomorrow AM (about 16 hours from now) when I’m able to dive further into the issue again. Hope you are well.

@ojus.zoom Here is the complete error, maybe this is overkill haha, it’s a lot:

Error downloading VTT file: AxiosError: Request failed with status code 401
at settle (file:///Users/danrobinson/Work/nxtlvlai/Post%20June%202022/bant-backend/node_modules/axios/lib/core/settle.js:19:12)
at IncomingMessage.handleStreamEnd (file:///Users/danrobinson/Work/nxtlvlai/Post%20June%202022/bant-backend/node_modules/axios/lib/adapters/http.js:570:11)
at IncomingMessage.emit (node:events:525:35)
at endReadableNT (node:internal/streams/readable:1358:12)
at processTicksAndRejections (node:internal/process/task_queues:83:21) {
code: ‘ERR_BAD_REQUEST’,
config: {
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
},
adapter: [ ‘xhr’, ‘http’ ],
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
xsrfCookieName: ‘XSRF-TOKEN’,
xsrfHeaderName: ‘X-XSRF-TOKEN’,
maxContentLength: -1,
maxBodyLength: -1,
env: { FormData: [Function], Blob: null },
validateStatus: [Function: validateStatus],
headers: AxiosHeaders {
Accept: ‘application/json, text/plain, /’,
Authorization: ‘Bearer eyJzdiI6IjAwMDAwMSIsImFsZyI6IkhTNTEyIiwidiI6IjIuMCIsImtpZCI6ImU5MWY4MzRiLTQ1NmQtNDQzYi05ZGMyLTU3ZjA2Mzg4ODFjOCJ9.eyJ2ZXIiOjksImF1aWQiOiI3Zjk1YzM1MDhjYTk1YzlmZjYxMmI5MDBiYTQ3OWI0MSIsImNvZGUiOiIwSFdxNTZiWG4zRDdkakJCZkszUmRPaVl5Sk5SMEEzMGciLCJpc3MiOiJ6bTpjaWQ6WjhBYmk2MmlTcjYyN3FzZUpBNmxhQSIsImdubyI6MCwidHlwZSI6MCwidGlkIjowLCJhdWQiOiJodHRwczovL29hdXRoLnpvb20udXMiLCJ1aWQiOiJna09mcmNWS1J3eTNVOFVMaFlxclNBIiwibmJmIjoxNjkzNDk4MDc5LCJleHAiOjE2OTM1MDE2NzksImlhdCI6MTY5MzQ5ODA3OSwiYWlkIjoiV2wyYWllaGVSV1Nla2c0NUhCel9EUSJ9.G_FlH5LHqGsXOCgoB19ddNzDvv9e3q_0a19gbEAnr0gzSPZrKJcazVFrdyeYEzWDjGesnvlhpSBcoaOHhXxVhQ’,
‘User-Agent’: ‘axios/1.4.0’,
‘Accept-Encoding’: ‘gzip, compress, deflate, br’
},
method: ‘get’,
url: ‘Passcode Required - Zoom’,
data: undefined
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype] {
abort: [Function (anonymous)],
aborted: [Function (anonymous)],
connect: [Function (anonymous)],
error: [Function (anonymous)],
socket: [Function (anonymous)],
timeout: [Function (anonymous)],
finish: [Function: requestOnFinish]
},
_eventsCount: 7,
_maxListeners: undefined,
outputData: ,
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
strictContentLength: false,
_contentLength: 0,
_hasBody: true,
_trailer: ‘’,
finished: true,
_headerSent: true,
_closed: false,
socket: TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
secureConnecting: false,
_SNICallback: null,
servername: ‘us06web.zoom.us’,
alpnProtocol: false,
authorized: true,
authorizationError: null,
encrypted: true,
_events: [Object: null prototype],
_eventsCount: 10,
connecting: false,
_hadError: false,
_parent: null,
_host: ‘us06web.zoom.us’,
_closeAfterHandlingError: false,
_readableState: [ReadableState],
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: ‘’,
server: undefined,
_server: null,
ssl: [TLSWrap],
_requestCert: true,
_rejectUnauthorized: true,
parser: null,
_httpMessage: [Circular *1],
[Symbol(res)]: [TLSWrap],
[Symbol(verified)]: true,
[Symbol(pendingSession)]: null,
[Symbol(async_id_symbol)]: 394,
[Symbol(kHandle)]: [TLSWrap],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: false,
[Symbol(kSetKeepAlive)]: true,
[Symbol(kSetKeepAliveInitialDelay)]: 60,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(connect-options)]: [Object],
[Symbol(RequestTimeout)]: undefined
},
_header: ‘GET /rec/download/uADH0wXSbWXGGGlZK-6AbFLpPf3Qaxivx1xAcCkYQhs9uL8XPwH2xKlqk2gQsv3c4sbXGCvswUSEjUZY.kzbOgZ2EYLptKGpM?type=cc HTTP/1.1\r\n’ +
‘Accept: application/json, text/plain, /\r\n’ +
‘Authorization: Bearer eyJzdiI6IjAwMDAwMSIsImFsZyI6IkhTNTEyIiwidiI6IjIuMCIsImtpZCI6ImU5MWY4MzRiLTQ1NmQtNDQzYi05ZGMyLTU3ZjA2Mzg4ODFjOCJ9.eyJ2ZXIiOjksImF1aWQiOiI3Zjk1YzM1MDhjYTk1YzlmZjYxMmI5MDBiYTQ3OWI0MSIsImNvZGUiOiIwSFdxNTZiWG4zRDdkakJCZkszUmRPaVl5Sk5SMEEzMGciLCJpc3MiOiJ6bTpjaWQ6WjhBYmk2MmlTcjYyN3FzZUpBNmxhQSIsImdubyI6MCwidHlwZSI6MCwidGlkIjowLCJhdWQiOiJodHRwczovL29hdXRoLnpvb20udXMiLCJ1aWQiOiJna09mcmNWS1J3eTNVOFVMaFlxclNBIiwibmJmIjoxNjkzNDk4MDc5LCJleHAiOjE2OTM1MDE2NzksImlhdCI6MTY5MzQ5ODA3OSwiYWlkIjoiV2wyYWllaGVSV1Nla2c0NUhCel9EUSJ9.G_FlH5LHqGsXOCgoB19ddNzDvv9e3q_0a19gbEAnr0gzSPZrKJcazVFrdyeYEzWDjGesnvlhpSBcoaOHhXxVhQ\r\n’ +
‘User-Agent: axios/1.4.0\r\n’ +
‘Accept-Encoding: gzip, compress, deflate, br\r\n’ +
‘Host: us06web.zoom.us\r\n’ +
‘Connection: close\r\n’ +
‘\r\n’,
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 443,
protocol: ‘https:’,
options: [Object: null prototype],
requests: [Object: null prototype] {},
sockets: [Object: null prototype],
freeSockets: [Object: null prototype] {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: ‘lifo’,
maxTotalSockets: Infinity,
totalSocketCount: 1,
maxCachedSessions: 100,
_sessionCache: [Object],
[Symbol(kCapture)]: false
},
socketPath: undefined,
method: ‘GET’,
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: ‘/rec/download/uADH0wXSbWXGGGlZK-6AbFLpPf3Qaxivx1xAcCkYQhs9uL8XPwH2xKlqk2gQsv3c4sbXGCvswUSEjUZY.kzbOgZ2EYLptKGpM?type=cc’,
_ended: true,
res: IncomingMessage {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 4,
_maxListeners: undefined,
socket: [TLSSocket],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: ‘1.1’,
complete: true,
rawHeaders: [Array],
rawTrailers: ,
aborted: false,
upgrade: false,
url: ‘’,
method: null,
statusCode: 401,
statusMessage: ‘Unauthorized’,
client: [TLSSocket],
_consuming: true,
_dumped: false,
req: [Circular *1],
responseUrl: ‘Passcode Required - Zoom’,
redirects: ,
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: [Object],
[Symbol(kHeadersCount)]: 24,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0,
[Symbol(RequestTimeout)]: undefined
},
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: ‘us06web.zoom.us’,
protocol: ‘https:’,
_redirectable: Writable {
_writableState: [WritableState],
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
_options: [Object],
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: ,
_requestBodyLength: 0,
_requestBodyBuffers: ,
_onNativeResponse: [Function (anonymous)],
_currentRequest: [Circular *1],
_currentUrl: ‘Passcode Required - Zoom’,
[Symbol(kCapture)]: false
},
[Symbol(kCapture)]: false,
[Symbol(kBytesWritten)]: 0,
[Symbol(kEndCalled)]: true,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
accept: [Array],
authorization: [Array],
‘user-agent’: [Array],
‘accept-encoding’: [Array],
host: [Array]
},
[Symbol(kUniqueHeaders)]: null
},
response: {
status: 401,
statusText: ‘Unauthorized’,
headers: AxiosHeaders {
date: ‘Fri, 08 Sep 2023 00:43:56 GMT’,
‘content-type’: ‘application/json;charset=UTF-8’,
‘transfer-encoding’: ‘chunked’,
connection: ‘close’,
‘x-zm-trackingid’: ‘v=2.0;clid=us06;rid=WEB_ac6e447846d42fdfa39cf75f212ecf38’,
‘x-content-type-options’: ‘nosniff’,
‘x-frame-options’: ‘SAMEORIGIN’,
‘cf-cache-status’: ‘DYNAMIC’,
‘set-cookie’: [Array],
server: ‘cloudflare’,
‘cf-ray’: ‘803328fb29f41f3e-DEN’,
‘alt-svc’: ‘h3=“:443”; ma=86400’
},
config: {
transitional: [Object],
adapter: [Array],
transformRequest: [Array],
transformResponse: [Array],
timeout: 0,
xsrfCookieName: ‘XSRF-TOKEN’,
xsrfHeaderName: ‘X-XSRF-TOKEN’,
maxContentLength: -1,
maxBodyLength: -1,
env: [Object],
validateStatus: [Function: validateStatus],
headers: [AxiosHeaders],
method: ‘get’,
url: ‘Passcode Required - Zoom’,
data: undefined
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
outputData: ,
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
strictContentLength: false,
_contentLength: 0,
_hasBody: true,
_trailer: ‘’,
finished: true,
_headerSent: true,
_closed: false,
socket: [TLSSocket],
_header: ‘GET /rec/download/uADH0wXSbWXGGGlZK-6AbFLpPf3Qaxivx1xAcCkYQhs9uL8XPwH2xKlqk2gQsv3c4sbXGCvswUSEjUZY.kzbOgZ2EYLptKGpM?type=cc HTTP/1.1\r\n’ +
‘Accept: application/json, text/plain, /\r\n’ +
‘Authorization: Bearer eyJzdiI6IjAwMDAwMSIsImFsZyI6IkhTNTEyIiwidiI6IjIuMCIsImtpZCI6ImU5MWY4MzRiLTQ1NmQtNDQzYi05ZGMyLTU3ZjA2Mzg4ODFjOCJ9.eyJ2ZXIiOjksImF1aWQiOiI3Zjk1YzM1MDhjYTk1YzlmZjYxMmI5MDBiYTQ3OWI0MSIsImNvZGUiOiIwSFdxNTZiWG4zRDdkakJCZkszUmRPaVl5Sk5SMEEzMGciLCJpc3MiOiJ6bTpjaWQ6WjhBYmk2MmlTcjYyN3FzZUpBNmxhQSIsImdubyI6MCwidHlwZSI6MCwidGlkIjowLCJhdWQiOiJodHRwczovL29hdXRoLnpvb20udXMiLCJ1aWQiOiJna09mcmNWS1J3eTNVOFVMaFlxclNBIiwibmJmIjoxNjkzNDk4MDc5LCJleHAiOjE2OTM1MDE2NzksImlhdCI6MTY5MzQ5ODA3OSwiYWlkIjoiV2wyYWllaGVSV1Nla2c0NUhCel9EUSJ9.G_FlH5LHqGsXOCgoB19ddNzDvv9e3q_0a19gbEAnr0gzSPZrKJcazVFrdyeYEzWDjGesnvlhpSBcoaOHhXxVhQ\r\n’ +
‘User-Agent: axios/1.4.0\r\n’ +
‘Accept-Encoding: gzip, compress, deflate, br\r\n’ +
‘Host: us06web.zoom.us\r\n’ +
‘Connection: close\r\n’ +
‘\r\n’,
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: [Agent],
socketPath: undefined,
method: ‘GET’,
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: ‘/rec/download/uADH0wXSbWXGGGlZK-6AbFLpPf3Qaxivx1xAcCkYQhs9uL8XPwH2xKlqk2gQsv3c4sbXGCvswUSEjUZY.kzbOgZ2EYLptKGpM?type=cc’,
_ended: true,
res: [IncomingMessage],
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: ‘us06web.zoom.us’,
protocol: ‘https:’,
_redirectable: [Writable],
[Symbol(kCapture)]: false,
[Symbol(kBytesWritten)]: 0,
[Symbol(kEndCalled)]: true,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype],
[Symbol(kUniqueHeaders)]: null
},
data: { status: false, errorCode: 124, errorMessage: ‘Forbidden’ }
}
}

here is the node express code I’m using that gets the error:

export const testZoomTranscript = async (req, res) => {
	console.log('HERE MADE IT');
	const downloadUrl =
		'https://us06web.zoom.us/rec/download/uADH0wXSbWXGGGlZK-6AbFLpPf3Qaxivx1xAcCkYQhs9uL8XPwH2xKlqk2gQsv3c4sbXGCvswUSEjUZY.kzbOgZ2EYLptKGpM?type=cc';
	const accessToken =
	const passcode = '@7*Cpkwo';
		try {
		const response = await axios.get(downloadUrl, {
			headers: {
				Authorization: `Bearer ${accessToken}`,
			},
		});

		const vttContent = response.data;
		console.log('VTT file content:', vttContent);
	} catch (error) {
		console.error('Error downloading VTT file:', error);
	}
};

i’ve removed the accessToken of course. the passcode isn’t used in the code. but when i got to the downloadUrl in my browser and enter that passcode in chrome, the vtt file is download. feel free to test that yourself if needed, the transcript is just me saying ‘test’, and does not contain sensitive information.

in case this matters, here is the url i’m going to in my browser to hit the route:
http://localhost:4000/zoom-test

here is the route in my code:
router.get(‘/zoom-test’, testZoomTranscript);

and the testZoomTranscript function that route calls is the function I gave above. Thank you! Hope you have a great night.

@chunsiong.zoom just wanted to tag you as it appears you may be more active in these forums. Hope you and everyone else on the team had a great weekend. Is there anything further I can do to help speed this process along?

Hi @nextlvlai ,

Please see the following: