Documentation

Schedule screens from a spreadsheet

A venue keeps its schedule in a spreadsheet: a screen code, a file name, a start and an end on every line. Import the sheet and screenRIG checks every row against your screens and media, names each problem by row and column, previews the calendar your screens would follow, and applies all of it or none of it.

From sheet to screens

  1. Start from the template. Save the template as CSV or XLSX, or use your own sheet with the same columns.
  2. Fill one row per schedule line. Name the screen (or a folder or a tag), the file, the start and the end.
  3. Import it. Drop the file on the Import dialog of the dashboard's Schedule tab, or run screenrig schedule import FILE.
  4. Correct the rows with errors. Each error names the row, the column and a code. Edit the cell in the dialog, or edit the sheet and import it again.
  5. Check the preview. It lists the levels the sheet makes, the ones it changes or removes, and the calendar of the screens it touches.
  6. Publish. All rows apply together or none does.

Steps 1 to 5 write nothing. A screen needs a time zone (screen set-timezone) and a default playlist (screen assign) before a row can schedule it, and a file must be an approved image or video.

The sheet

The first row names the columns. Each row after it is one schedule line. Blank rows are skipped. A sheet is a CSV file (UTF-8, comma or semicolon separated) or an XLSX workbook, and an XLSX workbook is read from its first sheet.

ColumnAlso read asWhat it holds
targetscreen, screen code, code, room, room id, screen name, screen idThe screens the row schedules. Required
contentfile, file name, media, asset, playlistWhat plays. Required
startstart date, from, beginWhen the row begins
endend date, until, stopWhen the row ends
timezonetime zone, tzThe zone the row's times are written in
daysweekdays, dayThe days of the week the row plays
daily startwindow start, time fromThe time of day each play window opens
daily endwindow end, time toThe time of day each play window closes
priority0 to 100. Higher plays over lower. Default 0
namelevel, labelA name for the level the row makes
displaydisplays, outputThe video wall outputs the row plays on, such as 1 or 1,2

Headers match ignoring case, spaces and punctuation. A column nothing reads is ignored, and the preview lists it. A row needs a target, a content and at least one of start, end, days or a daily window.

Screen,File,Start,End,Timezone,Days,Daily start,Daily end,Priority,Name
LOBBY-01,welcome-spring.jpg,2026-11-05 18:00,2026-11-08 02:00,America/Los_Angeles,,,,0,
folder:Casino Floor,happy-hour.mp4,2026-11-05,2026-11-30,,Mon-Fri,16:00,19:00,5,Happy hour
tag:elevators,playlist:Holiday loop,2026-12-01 09:00,2026-12-26,,,,,0,
  • Row 2 plays welcome-spring.jpg on the screen with the code LOBBY-01 from 6 PM on 5 November until 2 AM on 8 November, Pacific time.
  • Row 3 plays happy-hour.mp4 on every screen in the folder Casino Floor and in the folders inside it, from 4 PM to 7 PM on weekdays, from 5 November to the end of 30 November.
  • Row 4 plays the playlist Holiday loop on every screen with the tag elevators, from 9 AM on 1 December to the end of 25 December.

Targets

A target is one of these:

  • A screen. A screen id, the value of any field of the screen (a room code, a store number), or the screen's name. --code-field or code_field limits the match to one field, such as code or room_id.
  • A folder. folder:NAME or folder:Parent/Child. It reaches the folder and every folder inside it.
  • A tag. tag:NAME reaches every screen with the tag.

Several targets in one cell are separated by ; or |, as in LOBBY-01;LOBBY-02|tag:elevators.

Content

  • A file. The name of a media file in the project, or the original file name of a converted upload. The file plays full screen. The first commit that uses a file makes the one-file playlist Scheduled: FILE, and later imports reuse it.
  • A playlist. playlist:NAME or a playlist id. The playlist has an approved version and is not a template.

Times

  • Dates and times. 2026-11-05 18:00, 2026-11-05T18:00, 11/5/2026 6:00 PM and spreadsheet date cells are read. A time with an offset such as Z or +02:00 is refused.
  • Each screen's own clock. Times are read on the clock of the screens the row reaches, so 18:00 is 6 PM wherever the screen is.
  • Another zone. A timezone cell (or the request's timezone) says the row is written in that zone. The times convert to the screens' zone when all the row's screens share one zone. A row that reaches screens in several zones leaves timezone empty and reads each screen's own clock.
  • A date alone as the end closes that day: 2026-11-30 ends at the end of 30 November. A date alone as the start begins at midnight.
  • No end keeps the row playing until a later import removes it. The preview warns with open_end.
  • Days. Mon-Fri, Mon,Wed,Fri, Sat,Sun, weekdays, weekends or daily. A row with days and no daily window plays all day on those days.
  • A daily window. daily start and daily end give the hours of each day, such as 16:00 and 19:00 or 4:00 PM and 7:00 PM. Give both. An end at or before the start crosses midnight and belongs to the day it starts. Without days, the window applies every day.

Preview and errors

The preview checks every row and every cell before anything is written. A row with an error is shown with its cells and each error names the column, a code and what to change.

CodeColumnMeaning and fix
requiredtarget, content, daily start, daily endThe cell is empty, or only one of the daily window cells is filled. Fill it
unknown_screentargetNo screen has that id, field value or name. Check the code, or the field code_field names
ambiguous_screentargetThe value matches several screens. Use the screen id or a code that is unique
unknown_foldertargetNo screen folder has that name. Check the spelling
ambiguous_foldertargetSeveral folders have that name. Write the path, such as folder:Hotel/Casino Floor
unknown_tagtargetNo screen has the tag. Check the spelling
unknown_filecontentNo file or playlist has that name. Upload the file, or correct the name
ambiguous_filecontentSeveral files have that name. Rename one, or write playlist:NAME
not_playablecontentThe file is not an image or a video, the media waits for review, or the playlist is a template or has no approved version. Approve it, or choose another
invalid_timestart, end, daily start, daily endThe cell is not a date and time, or it carries a zone offset. Write 2026-11-05 18:00
reversed_timesendThe end is not after the start. Correct either
past_endendThe end has passed. Move it later, or remove the row
no_timingstartThe row says nothing about when. Give a start, an end, days or a daily window
invalid_valuedays, priority, displayThe cell is not a list of days such as Mon-Fri, a whole number from 0 to 100, or display numbers such as 1,2
timezonetimezoneThe zone is unknown, or the target holds screens in several zones. Use a zone such as America/Los_Angeles, or leave it empty
screen_not_readytargetA screen has no time zone or no default playlist. Run screen set-timezone or screen assign
overlappriorityTwo rows hold at the same time on a screen at the same priority. Give one a different priority, or move a time
limittargetA screen would hold more than 32 levels of its own, or the project more than 100 shared levels. Merge rows, or target a folder or a tag

Two warnings do not stop an import. open_end marks a row with no end. outranked marks a row that a shared level (a row for several screens, a folder or a tag) plays over on a screen.

A sheet with no rows, a file that is not UTF-8, more than 5,000 rows, a file over 8 MiB, or a commit that would write more than 400 screens' own schedules is refused with the reason.

Levels

Rows that agree on content, times, priority, name and display become one level. The target is not part of the comparison, so ten rows that play the same file at the same times on ten screens make one level.

  • One screen makes an entry in that screen's own schedule.
  • Several screens, a folder or a tag make one shared level, a campaign over that audience. A shared level outranks the own levels of a screen.

Grouping turns a venue's 200 screens into one campaign and not 200 entries, and a correction in the sheet changes one level. The name column names a shared level. Without one, it is called after the import, the content and the target, as in Spring promos: happy-hour.mp4 on Casino Floor.

Import again

The name of an import identifies it, and the default is import. Importing under the same name again:

  • updates the levels it made, even when a row's dates changed;
  • adds the levels of new rows;
  • removes the levels whose rows are gone from the sheet;
  • changes nothing when the sheet is the same.

Levels made by an import carry it in their id (a screen's entries start with imp_) and campaigns carry an import mark. Levels you made by hand are never touched. Use one name for one sheet, and a different name for another.

Run an import

Dashboard

Open a project's Schedule tab and choose Import. Drop a CSV or XLSX file. The dialog shows a table of the rows with each error beside its cell, and you edit the cell in place. It then shows the composed calendar of the screens the import changes. Publish commits it.

Command line

screenrig schedule template --output schedule.csv
screenrig schedule import schedule.csv --name "Spring promos" --timezone America/Los_Angeles
screenrig schedule import schedule.csv --name "Spring promos" --timezone America/Los_Angeles --commit

schedule import previews and writes nothing. --commit applies the sheet when every row is valid.

OptionMeaning
--name NAMEThe import's name, 1 to 80 characters. The default is the file name without its extension
--timezone ZONEThe zone the times are written in when a row has no timezone
--code-field FIELDRead a screen code only from this screen field
--commitApply the sheet
--format csv|xlsxschedule template: the template's format
--output FILEschedule template: where to save it

The command exits 0 for a valid sheet, 8 when rows have errors (nothing is written) and 5 when the project changed since the preview (run it again). FILE is a .csv, .xlsx or .json file, or - for standard input. A JSON file holds an array of row objects, or {"rows": [...]}, keyed by column name.

API

Send the sheet as JSON rows, or as the CSV or XLSX body with the settings as query parameters. Preview needs read access. Commit needs a credential with write access, which a person who can approve in the dashboard grants.

curl -s https://api.screenrig.ai/api/schedule/imports/preview \
  --header "Authorization: Bearer $SCREENRIG_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "name": "Spring promos",
    "timezone": "America/Los_Angeles",
    "rows": [
      {"target": "LOBBY-01", "content": "welcome-spring.jpg", "start": "2026-11-05 18:00", "end": "2026-11-08 02:00"},
      {"target": "folder:Casino Floor", "content": "happy-hour.mp4", "start": "2026-11-05", "end": "2026-11-30",
       "days": "Mon-Fri", "daily_start": "16:00", "daily_end": "19:00", "priority": 5, "name": "Happy hour"}
    ]
  }'

The same sheet as a file:

curl -s "https://api.screenrig.ai/api/schedule/imports/preview?name=Spring%20promos&timezone=America/Los_Angeles" \
  --header "Authorization: Bearer $SCREENRIG_TOKEN" \
  --header "Content-Type: text/csv" \
  --data-binary @schedule.csv

The answer, abridged:

{
  "name": "Spring promos",
  "valid": false,
  "fingerprint": "FINGERPRINT",
  "columns": ["target", "content", "start", "end", "days", "daily_start", "daily_end", "priority", "name"],
  "summary": {"rows": 2, "rows_with_errors": 1, "levels": 1, "creates": 1, "updates": 0,
              "unchanged": 0, "removes": 0, "screens": 1, "playlists": 1},
  "rows": [
    {"row": 2, "status": "ok", "level": "LEVEL_KEY"},
    {"row": 3, "status": "error",
     "errors": [{"column": "content", "code": "unknown_file",
                 "message": "No file or playlist is named \"happy-hour.mp4\"."}]}
  ],
  "levels": [{"key": "LEVEL_KEY", "action": "create", "home": "screen", "screen_id": "scr_LOBBY",
              "content": {"kind": "file", "id": "med_WELCOME", "name": "welcome-spring.jpg"},
              "screens": 1, "priority": 0, "from": "2026-11-05T18:00", "until": "2026-11-08T02:00", "rows": [2]}],
  "removals": []
}

rows[].cells returns each row's cells as the sheet gave them, so a client corrects them and sends the rows again. valid is true when no row has an error. calendar has the shape of GET /api/schedule/calendar and shows the screens the import changes as they would be after a commit.

Commit sends the same sheet and the fingerprint of its preview, as a member of the JSON body or a query parameter with a file. Send an Idempotency-Key to retry safely.

curl -s https://api.screenrig.ai/api/schedule/imports \
  --header "Authorization: Bearer $SCREENRIG_TOKEN" \
  --header "Idempotency-Key: spring-promos-1" \
  --header "Content-Type: application/json" \
  --data @commit.json

The answer holds the preview and applied, the counts of what was created, updated, left unchanged and removed, with the screens written and the playlists made. A commit is refused, and writes nothing, as:

ProblemStatusMeaning and fix
schedule_import_invalid422Rows have errors. Correct them and preview again
schedule_import_stale409Screens, media, playlists or levels changed since the preview, or the rows read differently. Preview again and commit with the new fingerprint

GET /api/schedule/imports/template?format=csv and format=xlsx answer the template.

Limits

  • 5,000 rows and 8 MiB per sheet.
  • 32 own levels per screen and 100 shared levels per project.
  • 400 screens' own schedules written per commit. A folder or tag row reaches any number of screens.
  • An import name is 1 to 80 characters.