"timeline" recording files return 403 AccessDenied at ssrweb.zoom.us — all other files in the same recording download fine

API Endpoint(s) and/or Zoom API Event(s)

  • List All Recordings — GET /users/{userId}/recordings

  • Get Meeting Recordings — GET /meetings/{meetingId}/recordings

  • File download via the download_url returned for each recording file (302-redirects to https://ssrweb.zoom.us/``...)

Description

We run a user-managed OAuth app that archives cloud recordings (RecordFlow, Zoom marketplace reviewed). For each meeting we call the recordings endpoint above, then download every file from its download_url with Authorization: Bearer <user token>, following redirects. Granted scopes include cloud_recording:read:list_recording_files and cloud_recording:read:recording.

Files with recording_type: "timeline" (the timeline.json artifact) consistently fail with HTTP 403, while every other file in the same recording downloads successfully via the identical code path and token.

This is type-specific and at scale:

  • Only timeline fails. In the same meetings these all succeed: shared_screen_with_speaker_view.mp4, gallery_view.mp4, active_speaker.mp4, audio_only.m4a, audio_transcript.vtt, summary.json, chat_file.txt.

  • ~2,160 failed timeline-download attempts over the last 7 days, 100% the same error (no mixed failure modes).

  • Affected recordings span 2024-08 through 2026-05, and timeline files from those same older dates succeed elsewhere — so there’s no date cutoff; failures cluster by meeting/account.

What I’ve already ruled out:

  1. Not our auth header — re-requesting the redirected ssrweb.zoom.us URL with no Authorization header at all returns the same 403, so the signed URL’s own credentials are rejected at the CDN, independent of our token.

  2. Not URL expiry — minting a fresh download_url and downloading immediately still 403s; ~1,180 fresh-URL retries over 7 days, 0% success.

  3. Not a missing artifact — the recordings response reports these files with a non-zero file_size (0.2–5.2 MB) and a valid download_url.

  4. Not scopes — a scope issue would be an API-level 401 before the redirect; we get a clean 302 and fail only at the CDN.

Questions:

  1. Why would a timeline file’s signed download_url return AccessDenied at ssrweb.zoom.us when every sibling file in the same recording downloads?

  2. Is there a different/required way to fetch timeline files (a distinct token, header, download_access_token, or include_fields parameter)?

  3. If the timeline object is genuinely unavailable for certain recordings, is there a field in the recordings response we can read to detect that up front and skip it?

I can share affected meeting UUIDs and the account ID privately via DM to any Zoom staff who can look them up.

Error?

HTTP/1.1 403 Forbidden
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message></Error>

How To Reproduce

Steps to reproduce the behavior:

  1. Request URL / Headers / Body

    • GET https://api.zoom.us/v2/meetings/{meetingId}/recordings Header: Authorization: Bearer <redacted user token>

    • In the response, locate the file object with "recording_type": "timeline" (non-zero file_size, valid download_url).

    • GET <download_url> with Authorization: Bearer <redacted> and follow redirects → 302 to https://ssrweb.zoom.us/...403 AccessDenied.

    • Repeat the redirected ssrweb.zoom.us GET with no Authorization header → still 403 AccessDenied.

  2. Authentication method / app type — User-managed OAuth app; user-level access token with cloud_recording:read:* scopes.

  3. Any errors403 AccessDenied (XML body above). Every other recording_type in the same response downloads with 200.