Backlot : An internal CMS for a streaming service
Films and series move through Backlot from upload to release in every region they go to. Two roles with separate permissions work in it: editors prepare content and metadata, publishers review it and decide what goes live. I designed both workflows end to end, including an AI check that flags likely rejections before the editor submits.

I started by mapping where Backlot sits in the content supply chain: between ingest (uploads, APIs, studio pipelines) and media processing and delivery. Most steps around it are automated. Editorial work and approval happen in the CMS, so that is where workflow design matters most.
Almost every field changes by region: title, synopsis, age rating, release date, rights window, even the attached audio and subtitles. One item holding every region's version would mean editing them all at once.
So the region comes first. The editor picks one, and everything under it (series, seasons, episodes, assets) shows for that region only.
Inside a region, the next question is what a single piece of content is. A film is one item. A series is a tree: seasons under the series, episodes under each season, files under each episode, and every level has its own metadata and status.
Some values roll up the tree. In each region, a season's age rating is the highest rating among its episodes, and the series takes the highest of its seasons. Others belong to one episode only, like its Spanish synopsis. The editor needs to see which is which, or a change lands on the wrong level.
A season with six approved episodes and two sent back is neither approved nor rejected, and I had to decide what it is. It shows as partly approved, and a release strategy decides whether it goes out now or waits for the last episode.
Mistakes rarely came from carelessness. They came from structure the editor can't see: an episode under the wrong season, metadata on the wrong level, the right asset on the wrong version. So I spent longer on the structure than on the screens.
Editors and publishers work on the same content, never at the same time. The editor sends an episode for review, the publisher finds something wrong and sends it back, the editor fixes it and sends it again.
The editor needs to know exactly what to fix. The publisher needs to see what changed since last time, without checking everything again. A rejection that doesn't say what's wrong only adds another round.
The work is not hard.
The loop is.
CMS tools are built in-house and rarely shown, so I studied products that each solve part of the same job: Contentful, Strapi, Directus, Frame.io, Dropbox Replay, Plex and Brightcove. Each does one piece well. None of them covers these three.
Each piece exists somewhere. What was missing is all of them in one place, around one content.
The editor's flow runs from choosing a series to submitting it for review. Automation sits in one step: after upload, the system reads technical metadata and assigns the files it can match, and the rest goes back to the editor. A wrong match is exactly the mistake editors can't see, so the system suggests and the editor confirms.
State organises the page, not the calendar.
Region is a scope, not a filter.
Designed for the delivery that arrives broken.

Uploads run in the background. The panel folds into the corner and the editor keeps working.
A ready file and an approved episode are not the same kind of true.
The page answers one question: what is left to do?
Before the editor submits, the page flags what this team's publishers usually send back.
The publisher approves an item, or sends it back with reasons the editor can act on one by one.
A rejection is a list of fixes, each pinned to its field.
The first version allowed one rejection reason per item, and I caught myself labelling rejected items “Multiple Issues”. That label was a symptom, not a reason, so reasons became a list, each pinned to a field and resolvable on its own.
Checks only prove the fields are filled. Judgement on ratings, artwork and sync is yours.
Approved content changes for a new reason, not because of a mistake.
Status tells you where the work is. Availability tells you what viewers can see. Two layers, and availability is never a ninth status.
Workflow status
To doIn progressPending reviewIn reviewApprovedScheduledPublishedRejectedThe release date is the only thing the team cannot move, so it is the only thing that raises urgency.
Only a source value can be wrong, so only a source value can be unlocked.
A score gives the editor nothing to act on. A named reason does, and every dismissal is kept.
Published has no hue, because most of the library ends up there. Amber and red mean urgency, nothing else.
Test it with editors. These decisions come from research and inference, not observation. I would test two things first: do the rejection categories cover what publishers actually send back, and do editors trust the split between fields they fill and fields the upload already filled.
Measure rejection rounds. If the pre-submit check works, the number of rounds per episode should drop. That is the first metric I would track.
Design what I scoped out. Unpublishing, bulk metadata editing and notification routing are each a small product of their own, and each one touches the same loop.
Research, flows, layouts and every product decision are mine. I used AI as a production tool, mainly to turn my screens into the animated walkthroughs on this page. The full system covers eight workflow states, three availability states, four field provenance treatments and three AI states.