Automating deletion of user-generated media
Automating UGC deletion means adding a scheduled delete step downstream of moderation with a grace period for appeals, making the step remove derived versions and backups as well as the original, and writing a log of every deletion. Storage is a current total, so the credit reduction appears immediately once the automation runs.
Before you start
You need a moderation decision already being recorded somewhere the deletion flow can read it. If uploads are moderated by hand, the human’s verdict has to land on the asset — a tag, a structured metadata field, or a moderation status — before anything here can act on it. If they are moderated by an automation, the MediaFlows moderation PowerFlow writes that state for you, and automating moderation of user-generated media before publication walks through setting it up.
Deleting rejected content is a separate concern from moderating it, and it should be a separate flow. Moderation is a workflow question: does this asset go live or not. Retention of rejected material is a legal question: how long you are obliged, or permitted, to hold something a user uploaded and you refused. Those two questions have different owners and change on different schedules, so wiring the delete into the moderation branch conflates them and means every legal review reopens the moderation flow.
You also need one number written down before you touch the builder: the grace period. Not a guess made in the flow editor — a period someone responsible for the site’s terms has signed off. Everything below hangs off it.
Steps
-
Decide the grace period and record it outside the flow.
A grace period between rejection and deletion is what makes an appeal possible. If a user’s upload is rejected and removed in the same pass, there is nothing left to review when they dispute the decision; you can tell them it was rejected, but you cannot show them what was rejected or reverse it. Immediate deletion forecloses appeal by construction. Pick a period, put it in the same document as the moderation policy, and treat the value in the flow as a copy of it, not the source. When the policy changes, the document changes first.
-
Set up the Delete UGC PowerFlow, triggered on the rejection state, not the upload.
Cloudinary’s Delete UGC PowerFlow is a prebuilt MediaFlows automation for removing user-generated assets. Point its trigger at the state your moderation step writes — the rejected tag or metadata value — rather than at upload events. Triggering on upload and filtering later works, but it means the flow fires for every accepted asset too and the reject condition lives in one more place than it needs to.
Put the grace period between trigger and delete as a delay, or as a scheduled sweep that only picks up rejections older than the period. The sweep pattern is easier to audit because the selection criterion is visible in one place; the delay pattern is easier to reason about per asset. Either is fine. What is not fine is a delete step with no wait in front of it.
MediaFlows is also drivable from its own MCP server, so the same flow can be built and later adjusted by an agent as well as by hand. That does not change the design; it changes who can edit it, which is worth knowing when you decide who is allowed to.
-
Make the delete cover derived copies and backups, not just the original.
A deletion request from a user — or a retention rule you set yourself — covers every copy: the transformations that were generated when the asset was viewed, any eager derivatives, and any backup copy, as well as the original. Only the original is obvious from the interface. Derived versions are created on demand and are not something anyone uploaded, so they are easy to forget exists; a backup, if enabled, is a further copy that outlives a plain delete.
In the flow, this means checking that the delete step is configured to invalidate and remove derived resources with the original, and that backed-up copies are handled by the same step or a following one, rather than assuming the asset is gone because it no longer appears in the media library. If you cannot see in the block’s configuration what happens to derived and backed-up copies, find out before shipping, because a request you believed you honoured but did not is worse than one you have not got to yet.
-
Log what was deleted, and when.
Add a step after the delete that writes a record: the public ID, the reason (rejected, expired, user request), the moderation decision it followed, the timestamp of the rejection, and the timestamp of the deletion. Send it somewhere outside Cloudinary — a webhook to your own system, a row in a sheet, a message to a channel that is retained. The automation should log every deletion, because after the asset is gone, the log is the only evidence that the retention policy was honoured. If a user asks whether their content was removed, or a regulator asks how long rejected uploads were held, the answer is that log or nothing.
Log the grace period actually applied per record, not just the current policy value. Policies change; the record needs to show what applied at the time.
-
Verify against usage.
Storage on Cloudinary is a current total, so a delete reduces storage credit consumption immediately. That is different from transformations and bandwidth, which are metered over a rolling 30-day window and only age out — the credits FAQ spells out the distinction. The practical use: after the first scheduled run, look at storage in the console. If it did not drop, the flow either did not fire or did not delete what you thought it deleted; go back to step 3 and check derived copies and backups. This is the cheapest end-to-end test of the whole automation and it costs no credits to perform.
The same fact tells you what deletion buys and does not buy. Removing rejected UGC recovers storage credit at once; it does nothing about the transformations and bandwidth those assets consumed while they existed, which stay in the 30-day window until they age out on their own.
Related choices
Rejected UGC is one retention case among several. Assets that were accepted but have simply reached the end of their useful life follow the same shape — a trigger, a grace period, a delete that covers derivatives, a log — with a different trigger condition, and automating asset expiry and retention covers that variant. If your delete flow is fed by an inbound webhook from your own moderation queue, verifying webhook signatures before a media automation acts on them matters more here than almost anywhere else, because a forged rejection event turns into a real deletion.
And there is a point where this stops being a no-code job. When the retention rules differ by jurisdiction, by user tier, or by content class, a visual flow with a branch per case becomes harder to audit than a small service with tests. The point where a media automation should become code is the honest place to make that call; a rule that a lawyer has to read is a rule that should be readable.
What done looks like
A rejected upload is not deleted immediately. It sits, in its rejected state, for the documented grace period, during which an appeal can still be reviewed against the actual asset. After the period, a scheduled MediaFlows run deletes the original, its derived versions and any backed-up copy in one pass. Each deletion produces a log record, stored outside Cloudinary, giving the asset identifier, the reason, the moderation decision, the rejection time and the deletion time. Storage usage in the console falls by the deleted total on the next check, and nobody has to remember to do any of it.
Sources
- MediaFlows moderation PowerFlowcloudinary.com
- Delete UGC PowerFlowcloudinary.com
- credits FAQcloudinary.com
See also
A hosted visual automation and a hand-written webhook handler weighed on setup cost, ongoing maintenance, who is allowed to edit, and version control.
Build a no-code webhook gate that preserves the raw body, rejects stale signatures, records event IDs, and prevents duplicate downstream actions.
How to check the signature and timestamp on a Cloudinary notification before any block in a media automation reads the payload, and why the order matters.
The signals that a visual media automation has outgrown its canvas — reviewability, branch count, loops with state, tests and cost — and what to move where.