Zoom API fails to delete all meeting recordings when requested

I was redirected here by Zoom Tech Support, so here we go:

Context
We’re interacting with the Zoom API from one of our web platforms, in order to schedule meetings, get recording links via webhooks, and also to archive those recordings as we go along.

And when they’ re archived, we delete them from the Zoom Cloud to free up space.

Issue
We have recurring “leftovers”, and as it happens, it seems the Zoom API doesn’t do its job properly when a meeting has multiple recordings.

Example with this API call log (links partly redacted to allow posting):

04:20:54 PM
May 28, 2024
	204 	v2/meetings/8640238xxxx/recordings 	
{
"endpoint": "v2/meetings/8640238xxxx/recordings",
"method": "DELETE",
"response": "N/A",
"response_headers": "{}",
"date_time": "2024-05-28T14:20:54.615+00:00",
"request_body": "N/A",
"request_headers": "{x-zm-haid=93, x-zm-real-ip=xxxx, x-forwarded-for=xxxx, x-zm-cluster-id=us02, x-zm-aid=fppoBdxxxxx, user-agent=python-requests/2.31.0}",
"request_params": "{meeting_id=8640238xxxx}",
"http_status": "204"
}

As can be seen above, we’re resorting to the “Delete meeting recordings” API endpoint (/docs/api/rest/reference/zoom-api/methods/#operation/recordingDelete), which is supposed to “Delete all recording files of a meeting.”

Except it doesn’t. It deletes one, and leaves the rest as is somehow. As part of investigations, I also see that we get a “204” response and not a “200” for some undetermined reason.

Would you have any ideas on why we are encountering this behavior ? Based on “recording deletion” topic history on the subject, this seems like a recurring issue, usually ending on a “let’s switch to DMs” from Zoom reps (hopefully I’ll be granted one of those ?)

After digging into this topic (yay, can now post links !), I wonder if the issue might be related to the recent “Granular” scope changes, that seem to be very partially documented, especially regarding recording deletion.

As our config pre-dates those changes, it is using the now-classic scopes (and does have all three recording:*scopes enabled)

Well, might have to resort to the “Delete a meeting recording file” endpoint and cycle through all recordings to have them deleted one by one, then ?

If I read the doc right (some of it is actually showing HTML tags), I’ll have to check/apply some settings if want to give this a chance to work.

I’d rather have “Delete meeting recordings” work, though…

Hi @it_epsn ,

This sounds like a bug. Please provide the following in the private message I will initiate with you. Check your notifications for the message. Thanks!:

  • approximate date when app was created
  • app type (if relevant)
  • dev email associated with credentials making API request
  • client id
  • full API request
  • API response screenshot
  • API response zm-tracking-id (found in the response headers)
  • recording or screenshot evidence of issue (you can use Zoom to record and send link)
1 Like

Hi @it_epsn ,

From docs “If the meeting ID is provided instead of UUID, the response will be for the latest meeting instance,” but please confirm if this is the same meeting a start and stop of multiple recordings. The screenshots you provided shows the recordings happened on the same day, but at different start times. One of the recordings is in the trash while the other is not. If these were actually two separate meetings with the same meeting id (i.e. a recurring meeting or PMI meeting), you may need to pass the uuid for each meeting.

I’ve opened up with support and your ticket is TS1260299. Please check the email you provided to correspond with support and let us know here when you have the answer :slight_smile:

1 Like

Hi @gianni.zoom ,

Thanks very much for following up. The meetings experiencing the issue are non-recurring meetings. Unsure what a PMI meeting is exactly, but we do create our meetings through the API, for “user” accounts we associated to classrooms on our end.

We do have 1 meeting for each half-day course on our platform. Sometimes, the following happens:

  • meeting is launched and closed soon after, then re-launched (likely because the teacher mishandled the Zoom Client)
  • meeting is stopped and restarted in relation to the usual pause of the half-day course

Good thing that you point the documentation, as it is actually rather confusing.

You are likely citing this line from “Delete meeting recordings” path parameters ?

To get Cloud Recordings of a meeting, provide the meeting ID or meeting UUID. If the meeting ID is provided instead of UUID,the response will be for the latest meeting instance.

The 1st sentence is quite strange, as we aren’t seeking to “get Cloud Recordings” in that context, but actually delete them.

If we look at the path parameters part of “Get meeting recordings”, we have pretty much the same sentence (save a few upper cases and word reordering):

To get a meeting's cloud recordings, provide the meeting ID or UUID. If providing the meeting ID instead of UUID, the response will be for the latest meeting instance.

It feels like there might have been a copy-paste from “Get” to “Delete” that’s never been properly updated. An edit would be a good opportunity to detail the required “Granular Scopes” for the two “Delete” endpoints related to recording (the " Delete a meeting recording file" one suffers from the same “path parameter” thing, btw).

In all cases, I had already read on “meeting instance” to try and clarify to what they refer to , but it seemed to be tied to recurring meetings, which we don’t use.

But whether those are the cause/reason of the issue or not, and from a logical standpoint, it would seem quite reasonable to expect “Delete meeting recordings” to delete the same recordings that the ones returned by " Get meeting recordings" when both endpoints are given an identical Meeting ID.

Looking forward to hear about TS1260299 (no trace of it yet on my end). I heard back from the responder of TS1253058, though (but only to be told once more to come here for help :wink: )

Hi @it_epsn , I opened the ticket with the email you provided in the direct message. Do you see it there?

Thanks @gianni.zoom , the ticket is now visible, and I added myself to the watchlist to facilitate interactions. I’ll keep you (and visitors) posted here !

1 Like

Allright, here’s a 1st feedback on what has been clarified and understood.

TL;DR
If you’re interacting with (non-recurring) meetings that have multiple recordings, don’t just think you can easily use “Get/Delete meeting recordings” endpoints. You can’t.

First, misconceptions/mistakes:

  • I misinterpreted our code, and thought it was using “Get meeting recordings” to get the recording list. It’s actually using the “List all recordings” endpoint
  • I assumed we weren’t concerned by “meeting instances” and associated UUIDs, based on those details that only cite “recurring meetings” as example situations where meeting instances would be generated
  • Overall, I inconsciously assumed way too many things from the endpoint’s names

Despite using scheduled, non recurring meetings, it appears that our user behavior still provokes generation of multiple meeting instances, spreading recordings across those. This prevents an “easy” use of “Get/delete meeting recordings”, as they will only return/delete the latest recording, tied to the latest meeting instance, when using the meeting ID.

As working with UUIDs will also return only one recording set, I wonder why the endpoints are using the plural for “recordings”, and why there is a “200” status on " Delete meeting recordings" when the API only ever responds with 204.

Anyway, main lessons for me here are:

  • If you have multiple recordings, assume they are each associated to their own meeting instance
  • Don’t assume endpoints will have a global consistent behavior when using a meeting ID/number

Typically, if “List all recordings” (that would be more aptly named something like “List user recordings”) is given a meeting ID and a sufficiently old start date, it will return all recordings for that ID, no matter the instances.

Then, doc/API inconsistencies (some already mentioned in previous messages):

  • The “Delete” recording endpoints docs are both insuficient/inadequate, incomplete, wrong/contradictory and obsolete
  • The “Get meeting recordings” docs are wrong/contradictory - Says it will " Returns all of a meeting’s recordings." and will only ever do that if there is only a single recording involved
  • Inconsistent use of “Field” parameters, mixing/confusing id, uuid, meeting_id , further misleading when considering “Path” parameters (more on that below)

So, as already pointed, various endpoints request to add a {meetingId} in the request path, when they really need you to provide a UUID (if a meeting ID is provided, it is just “translated” to the UUID of the latest instance). You’ll also have to conditionally (why) “double encode” said UUID - up to you to figure out what that means and how to do it programatically (tip: you’ll have a “urlencode” method for that in some languages).

Then, if/when you get a response, you’ll have to decypher in context what the fields actually mean.

Say you “Get meeting recordings” and want to do something with its “recording_files”: Can you just take its “meeting_id” and assume it actually is a meeting ID ? No, because in practice, what is returned here is actually a meeting instance’s UUID.

So from there, don’t use that “meeting_id” for use with " List meeting polls" as {meetingId}, because you’ll be giving a UUID to an endpoint actually requiring a meeting ID.

However, if you want to " Get past meeting participants" and pass that “meeting_id” field as {meetingId} in its path, it will be ok, as this {meetingId} is actually expecting a UUID.

Same if you want to “Recover meeting recordings”, you’ll be able to use that “meeting_id” to fill the {meetingUUID} path placeholder. Fun, eh ?

So, to summarize, an actual meeting ID/number may be stored/found in fields named “id” , but never in “meeting_id” for responses. They will most likely be successfully used in paths mentioning {meetingId}, oftentimes by being translated to the latest UUID of that meeting ID

An actual meeting UUID may be stored/found in fields named “uuid” and “meeting_id”. They will most likely be successfully used in paths mentioning {meetingId} that actually require a UUID, and paths {meetingUUID}

(temporary) Conclusion
As soon as you have more than 1 recording on a meeting, you can’t just hope to simply use “Get/Delete meeting recordings” endpoints to interact with them.

You either have to:

  • resort to " List all recordings", cycle through the recordings and use the meeting_ids_that_actually_are_uuids to delete them one by one
  • “List past meeting instances”, try and “Delete meeting recordings” on each uuid and maybe do one more round with the meeting ID to handle the current/latest instance.

Hi @it_epsn , thank you for your in-depth analysis and identifying some areas of improvement for our documentation!

A few more details following the support’s latest feedback (thanks to Otho for the help in digging down the issue), with the hope to help further clarify for readers:

Meeting instances & recordings

Per my understanding, as soon as you close and relaunch a meeting (i.e. close the meeting or the Zoom Client), you’ll get distinct meeting instances for your recordings. No matter if your meeting was scheduled from 6AM to 10PM, with all start/stop operations happening within the meeting’s scheduled timeframe.

The only case where you may have multiple recordings within one single instance is if you keep the meeting open/active, and only stop/restart recording within that meeting execution.

Meeting IDs and UUIDs in the API

To extract info from my previous rant and for better readability, a few tips at the time of this post (hopefully, the documentation will evolve favorably).

Also, just in case, if using recurring meetings : occurrence_id UUID

For “PATH PARAMETERS”, when asked for :

  • A {meetingId} in the context of meeting-related endpoints-> An actual Meeting ID
  • A {meetingId} in the context of recording-related endpoints-> A UUID is likely needed - may accept an ID that will be translated to the latest UUID of this meeting
  • A {meetingUUID} → definitely a UUID (I hope - didn’t use/confront/test)

For Fields in object responses:

  • An id → Meeting ID (or recording ID, or registrant ID, etc…)
  • A uuid → Meeting UUID
  • A meeting_id_> Not actually an ID, but a Meeting UUID

Warning ! In the context of “QUERY-STRING PARAMETERS” (for example with the List all recordings endpoint) , the meeting_id parameter is actually expecting a Meeting ID, and no UUID.

So you give a Meeting ID through the meeting_id parameter when calling the endpoint, but the meeting_id field in the recording_files you get in response are UUIDs. Even if the doc says otherwise.

1 Like