Documentation/Reference apps/Backlot, a film production scheduling app with no call-sheet table
Backlot, a film production scheduling app with no call-sheet table
Backlot is an open-source film production scheduling app: slate, crew, strip board, call sheets by email and SMS. On GitHub, live at backlot.supero.live.
- apps
- media
- film-production
- scheduling
- workflows
Overview
In this open-source film production scheduling app the production office writes the rows, and each member of cast or crew receives only the ones addressed to them.
Backlot publishes its slate, the list of productions, and keeps everything else behind a login. Two read-only GET requests, sent without signing in, let you check that for yourself:
bash
curl -s https://backlot.supero.live/api/public/production
curl -s -o /dev/null -w "%{http_code}\n" https://backlot.supero.live/api/public/assignmentOn 2 October 2026 the first returned a JSON list of six productions and the second printed 403, with a body saying the schema is not public, and the same unauthenticated request for the
person schema got the same answer.The slate is the shop window. Who is called where, at what hour and for what rate, sits behind a login, and even then each person gets only their own part of it.
An assignment is written by one person and owned by another
schemas.py, in the repo's backlot folder, has five schemas, one per table. They cover the slate, the roster of people, the scenes, the shoot locations and the assignments. None is called a call sheet. An Assignment names a production and a person, gives a role with a call time and a location, states a rate, and ends with an owner_username. When the office offers a role in the studio console, the save handler in ui/app.js copies person_email into owner_username. The row is created by the line producer. It belongs to the cinematographer.The crew side is then almost nothing. "My call sheet" calls
client.getObjects('assignment'), sorts by call time and groups by state. It contains no filter by user. The filter lives in setup.py:python
PolicyDef(role="tenant_user", default_access="none", rules=[
# Crew/cast can read the slate, the roster, the schedule and locations…
PolicyRule(entity="production", can_read=True),
PolicyRule(entity="person", can_read=True),
PolicyRule(entity="scene", can_read=True),
PolicyRule(entity="shoot_location", can_read=True),
# …but only see & act on THEIR OWN call sheet (owner-scoped to their email).
PolicyRule(entity="assignment", can_read=True, can_create=True, can_update=True,
filter_field="owner_username", filter_match="$user.name"),
]),That last rule is policy-based row scoping. The seed writes twelve assignments, and five of them carry the demo crew account's email. So that login has a five-row call sheet: two rows confirmed, two offered, one declined.
Offered, confirmed, declined, wrapped
python
SCENE_STATES = ["not_scheduled", "scheduled", "shot", "needs_reshoot"]
LOCATION_STATES = ["scouting", "secured", "released"]
ASSIGNMENT_STATES = ["offered", "confirmed", "declined", "wrapped"]Backlot's is the only schema file among the 19 apps in the repo with an
offered state. The handshake runs in both directions. The office creates the row at offered, and an event binding on @create:backlot:assignment runs the assignment_offer workflow to email the person. The crew member then presses Confirm or Decline on their own row.That update is the only write the crew portal makes.
The office can also confirm from its side with the "Call sheet" button. It runs a workflow named
call_sheet, declared with "on_error": "compensate", whose three steps set assignment_state to confirmed, email the call time with the location, and send the same details by SMS. The first step carries a compensation that writes back the prior_state the console passed in, so that a failed email is meant to leave the assignment where it was instead of showing a confirmation nobody received. The SMS step is marked "on_error": "continue" and cannot undo the other two. A third workflow, wrap_notify, sets wrapped and sends a thank-you.Film production scheduling from one table and a sort
The schedule tab is called the strip board.
StudioSchedule loads every Scene, sorts by shoot_date and groups by day in the browser, with any scene that has no date at the end. Each strip shows the scene number and location, whether it is INT or EXT, the time of day and a page count. Buttons on the strip mark a scene shot or flag it for a reshoot. The seed has twelve scenes across four productions. One of them, scene 7B of "Paper Tigers", is seeded at needs_reshoot with a note that the camera move missed the handoff.The console has seven tabs. The first is a dashboard. The six seeded budgets add up to $123.2 million, and the Slate budget tile rounds that to "$123M". The last is an assistant that sends
services.ai.complete a prompt containing up to 30 productions from the slate, each line carrying the stage, the director, the budget and the logline. The code names no model. When the call is unavailable or comes back empty, a function called deterministicAnswer matches the question against a few patterns (logline, schedule, budget) and answers from the slate data directly.Roll your own production office
Two demo accounts are listed on the login screen at backlot.supero.live, one for the production office and one for crew. They map to the only two roles in
setup.py: tenant_admin for the office, tenant_user for everyone on the call sheet. Offer the crew account a role from the office side. Then sign in as crew and find it waiting.To run it locally:
bash
git clone https://github.com/supero-platform/supero-apps
cd supero-apps/apps/media/backlot
cp .env.example .env # set SUPERO_DOMAIN and SUPERO_PASSWORD
./run.shNobody has to create an account beforehand. Pick an unused domain name in
.env, and about two minutes later you have six productions, sixteen people and six locations to rearrange. The backlot folder is MIT licensed, front end included. The platform it calls is hosted and is not open source.Put your own slate in
PRODUCTIONS. For a different kind of crew entirely, a new schema starts at https://www.supero.dev.On this page