Set up through REST
Create the resources through REST, then open the returned control-room URL. This flow supports code graphics and native Rive graphics.
You need an organization and an API key.
The key needs themes:write, competitions:write, teams:write, venues:write, matches:write, and overlays:write.
Each write scope also allows reads for that resource.
The person opening the control room needs an authenticated dashboard session with access to the same organization.
1. Publish the graphic
Section titled “1. Publish the graphic”Follow Publish without the CLI to create the theme, upload the bundle, and publish the graphic.
For native Rive, follow Import through REST through theme activation.
Publish a theme version, then activate that exact version through REST.
Save the theme’s numeric id as THEME_ID and the graphic’s UUID as GRAPHIC_ID.
| Operation | Route |
|---|---|
| Create the theme | POST /v2/themes |
| Create the graphic | POST /v2/themes/{themeId}/code-graphics |
| Request signed upload URLs | POST /v2/themes/{themeId}/code-graphics/{graphicId}/uploads |
| Upload files | PUT each returned upload URL with the file body |
| Record the files and manifest | PUT /v2/themes/{themeId}/code-graphics/{graphicId}/bundle |
| Publish the graphic | POST /v2/themes/{themeId}/code-graphics/{graphicId}/versions |
| Publish the theme snapshot | POST /v2/themes/{themeId}/versions |
| Activate that snapshot | PUT /v2/themes/{themeId}/active-version |
The remaining examples use Bash, curl, and jq. Run them in one terminal.
Set LIGR_API_KEY, THEME_ID, and GRAPHIC_ID from your authentication and publishing steps.
set -euo pipefailexport LIGR_API_URL="${LIGR_API_URL:-https://api.ligr.live/rest/v2}"export LIGR_REST="${LIGR_API_URL%/v2}"
api() { local method="$1" route="$2" shift 2 curl --fail-with-body --silent --show-error \ -X "$method" "$LIGR_REST/$route" \ -H "Authorization: Bearer $LIGR_API_KEY" \ -H 'Content-Type: application/json' "$@"}Set LIGR_API_URL to your target environment before continuing. The Rive tutorial and the CLI read the same variable.
The default is https://api.ligr.live/rest/v2.
This page calls v1 and v2 routes, so LIGR_REST removes the /v2 suffix.
2. Create the room and preset
Section titled “2. Create the room and preset”A room belongs to your organization and one theme. A section groups its preset buttons.
ROOM_ID=$(api POST v2/control-rooms --data "$(jq -n \ --argjson themeId "$THEME_ID" \ '{themeId: $themeId, name: "Match control room"}')" | jq -er '.id')
SECTION_ID=$(api POST "v2/control-rooms/$ROOM_ID/sections" \ --data '{"name":"Match graphics"}' | jq -er '.id')
PRESET_ID=$(api POST "v2/control-rooms/$ROOM_ID/presets" --data "$(jq -n \ --arg sectionId "$SECTION_ID" --arg graphicId "$GRAPHIC_ID" \ '{sectionId: $sectionId, graphicId: $graphicId, name: "Scorebug", variableValues: {}}')" \ | jq -er '.id')The graphic must appear in the theme’s active published version.
Preset creation reads that published manifest, including its variable types and defaults.
Only variables named in variableValues or exposeVariables appear as editable fields on the preset.
The API stores the internal preset structure for you.
Set variableValues by manifest variable name, for example {"CustomText":"Centre Court"} when your graphic defines CustomText.
variableValues accepts string, number, boolean and enum variables.
Name fact, stat, team, player and match variables in exposeVariables; the preset shows them and the operator picks the value live.
Do not send hide; the show and hide actions control visibility.
Unknown names and invalid values fail before the API creates the preset.
api POST "v2/control-rooms/$ROOM_ID/presets" --data "$(jq -n \ --arg sectionId "$SECTION_ID" --arg graphicId "$GRAPHIC_ID" \ '{sectionId: $sectionId, graphicId: $graphicId, name: "Scorebug", variableValues: {AddedTime: "0"}, exposeVariables: ["Event", "SingleStat"]}')" | jqThe response lists exposedVariables: every variable the operator can see, with or without a stored value.
To change a preset later, send PUT /v2/control-rooms/{roomId}/presets/{presetId}.
Send name or sectionId alone to rename or move it.
Send variableValues and exposeVariables together to replace its variable set; the stored set becomes exactly what you send.
To reuse resources, list rooms with GET /v2/control-rooms?themeId={themeId}.
List their sections and presets with GET /v2/control-rooms/{roomId}/sections and GET /v2/control-rooms/{roomId}/presets.
3. Create the competition, venue, and teams
Section titled “3. Create the competition, venue, and teams”Discover competitions through GET /v1/competitions and venues through GET /v1/venues.
List a competition’s teams with GET /v1/competitions/{competitionId}/teams.
Use the returned IDs to skip the corresponding creation calls.
List grades through REST. Set GRADE_ID to the returned ID that matches your competition.
The example uses adult mixed football; change the metadata for your competition.
api GET v1/competitions/grades | jq .# Set GRADE_ID to the selected numeric id before continuing.
COMPETITION_ID=$(api POST v1/competitions --data "$(jq -n \ --argjson gradeId "$GRADE_ID" \ '{name: "My competition", sport: "football", age: "Adults", gender: "Mixed", gradeId: $gradeId}')" \ | jq -er '.id')
VENUE_ID=$(api POST v1/venues --data '{"name":"Main ground"}' | jq -er '.id')
create_team() { api POST v1/teams --data "$(jq -n \ --arg name "$1" --arg abbreviation "$2" \ --argjson gradeId "$GRADE_ID" --argjson competitionId "$COMPETITION_ID" \ --argjson venueId "$VENUE_ID" \ '{name: $name, abbreviation: $abbreviation, sport: "football", age: "Adults", gender: "Mixed", gradeId: $gradeId, competitionIds: [$competitionId], defaultVenueId: $venueId}')" | jq -er '.id'}
HOME_TEAM_ID=$(create_team 'Home team' HOME)AWAY_TEAM_ID=$(create_team 'Away team' AWAY)Set a team logo
Section titled “Set a team logo”Graphics show the team logo as logoUrl. Upload the logo in two calls.
The API does not fetch images from a URL.
Use a PNG or JPEG file of 500 KB or less. Declare its type, size, and SHA-256 first.
The response holds uploadId, a signed url, and the headers for the upload.
LOGO=home-crest.pngSESSION=$(api POST "v1/teams/$HOME_TEAM_ID/logo/uploads" --data "$(jq -n \ --argjson size "$(wc -c < "$LOGO" | tr -d ' ')" \ --arg sha256 "$(shasum -a 256 "$LOGO" | cut -d ' ' -f 1)" \ '{contentType: "image/png", size: $size, sha256: $sha256}')")
curl --fail-with-body --silent --show-error -X PUT "$(jq -er '.url' <<< "$SESSION")" \ -H "x-amz-checksum-sha256: $(jq -er '.headers["x-amz-checksum-sha256"]' <<< "$SESSION")" \ --data-binary "@$LOGO"
api PUT "v1/teams/$HOME_TEAM_ID/logo" --data "$(jq -n \ --argjson uploadId "$(jq -er '.uploadId' <<< "$SESSION")" '{uploadId: $uploadId}')" | jq '{id, logoUrl}'Do the PUT before expiresAt. Send the uploadId within one hour.
The API checks the size, the SHA-256, and the image type, then stores the logo.
Each upload sets a logo one time. To change the logo, open a new upload.
To remove the logo, send DELETE /v1/teams/{teamId}/logo.
A team without a logo shows the club logo, if the club has one.
4. Create the match and theme profile
Section titled “4. Create the match and theme profile”Set MATCH_DATE to your kickoff time in ISO 8601 format, including its timezone.
The example uses team competitors. Other sports can use player competitors.
competitorsType is the match format: singles, doubles, teams or group.
entityType is the kind of each row in competitors: team or player.
For example, a doubles match has competitorsType: doubles and four entityType: player rows.
A group match also has player rows.
MATCH_ID=$(api POST v2/matches --data "$(jq -n \ --arg date "$MATCH_DATE" --argjson competitionId "$COMPETITION_ID" \ --argjson venueId "$VENUE_ID" --argjson home "$HOME_TEAM_ID" --argjson away "$AWAY_TEAM_ID" \ '{competitionId: $competitionId, venueId: $venueId, date: $date, competitorsType: "teams", competitors: [ {entityId: $home, entityType: "team", meta: {isHome: true}}, {entityId: $away, entityType: "team", meta: {isHome: false}} ]}')" | jq -er '.id')
PROFILE_ID=$(api POST "v2/competitions/$COMPETITION_ID/theme-profiles" --data "$(jq -n \ --argjson themeId "$THEME_ID" --argjson roomId "$ROOM_ID" \ '{themeId: $themeId, name: "Broadcast profile", defaultControlRoomId: $roomId}')" | jq -er '.id')The theme must support the competition’s sport and have an active published version. The first profile becomes the competition default. This flow also assigns the profile and room explicitly to the overlay.
To set competition-specific colors or labels, include themeVariableValues when creating the profile.
Read variables from GET /v2/themes/{themeId}/versions/{activeVersion}.
Use those variable IDs, for example [{"variableId":"accent","value":"#0055ff"}].
The response returns the saved values. Unknown IDs and invalid types fail before creation.
To reuse a profile, call GET /v2/competitions/{competitionId}/theme-profiles.
5. Create the manual overlay
Section titled “5. Create the manual overlay”OVERLAY=$(api POST v1/overlays --data "$(jq -n \ --argjson matchId "$MATCH_ID" --argjson profileId "$PROFILE_ID" --argjson roomId "$ROOM_ID" \ '{name: "Broadcast overlay", matchId: $matchId, competitionThemeSettingId: $profileId, controlRoomId: $roomId, autoMode: false, adType: "noBrands"}')")
OVERLAY_ID=$(jq -er '.id' <<< "$OVERLAY")CONTROL_ROOM_URL=$(jq -er '.controlRoomUrl' <<< "$OVERLAY")api GET "v1/overlays/$OVERLAY_ID" | jq '{id, autoMode, adType, competitionThemeSettingId, controlRoomId, controlRoomUrl}'printf '%s\n' "$CONTROL_ROOM_URL"The read response confirms the saved profile, room, and manual mode.
controlRoomUrl includes the organization, match, overlay, and room IDs.
It opens the saved setup without a theme-profile editor or overlay-settings visit.
autoMode: false requires adType: "noBrands" or "brands".
The API rejects manual setup with "free".
An update preserves fields you omit, including the ad type.
6. Open the finished control room
Section titled “6. Open the finished control room”Open CONTROL_ROOM_URL in your authenticated browser.
Select GFX In on the preset and check the preview.
If the preset exposes variables, edit them and select Update GFX.
Select GFX Out to hide it.
You can also fire the same prepared preset through REST:
api POST "v2/overlays/$OVERLAY_ID/control-room/graphics" --data "$(jq -n \ --argjson presetId "$PRESET_ID" '{action: "show", presetId: $presetId}')"7. Check a prepared room without a dashboard login
Section titled “7. Check a prepared room without a dashboard login”Use this procedure when you cannot open the dashboard, for example from an automation.
The key needs themes:read to read the room, overlays:read to see its overlays, and overlays:write to fire presets.
-
Read the room. The response holds the theme, the sections, and every preset with its
graphicId,graphicNameandexposedVariables.Terminal ROOM=$(api GET "v2/control-rooms/$ROOM_ID")jq '{theme, presets: [.presets[] | {id, name, graphicName, exposedVariables}]}' <<< "$ROOM" -
Open the monitoring view of the overlay. Read
monitoringUrlfrom the room; do not build it. The room listsoverlaysonly when the key hasoverlays:read.Terminal jq -er --argjson id "$OVERLAY_ID" '.overlays[] | select(.id == $id) | .monitoringUrl' <<< "$ROOM" -
Fire each preset and check the monitoring view after each command.
Terminal for PRESET in $(jq -r '.presets[].id' <<< "$ROOM"); doapi POST "v2/overlays/$OVERLAY_ID/control-room/graphics" --data "$(jq -n \--argjson presetId "$PRESET" '{action: "show", presetId: $presetId}')" > /dev/nullread -r -p "Preset $PRESET on air? Press Enter to continue."done -
Update a preset that is on air. Send the values to change in
variableValues.Terminal api POST "v2/overlays/$OVERLAY_ID/control-room/graphics" --data "$(jq -n \--argjson presetId "$PRESET_ID" '{action: "update", presetId: $presetId, variableValues: {CustomText: "Court 2"}}')" -
Hide each preset.
Terminal api POST "v2/overlays/$OVERLAY_ID/control-room/graphics" --data "$(jq -n \--argjson presetId "$PRESET_ID" '{action: "hide", presetId: $presetId}')"
See Check what is on screen if a graphic does not appear.
Theme versions
Section titled “Theme versions”Publishing another theme version does not change the active version unless you request activation. Use version inspection, activation, and rollback through REST. Reload existing overlay pages after changing the active version.