Skip to content
Guide

Create a display and give it content

Roll out displays from your own list: create each one with name and location, assign a program, and hand over the address for the TV.

3 min readUpdated Oct 30, 2026As Markdown
For
Developers who set up displays from their own system
Prerequisite
a key ak_… of the class Custom with fleet.web-display.manage, location.list, and schedule.update, and the Displays without an agent capability for your organization
Where
command line
On this page

By the end, a new display is in your fleet with its name and location, it already has a program, and the TV only needs to open one address. This is how you run a rollout from your own list, such as the branches in a spreadsheet.

Why a web display

A web display needs no device of its own. The API creates it in one call, and a person opens its address in the browser of the TV. Devices that show a six-character code are paired with POST /v1/pairings/claim instead. That needs the permission fleet.player.pair, which no API key can carry, so pairing a device stays with a person signed in to the portal.

Steps

  1. Find the location. GET /v1/locations returns every location of your organization in one response, without paging:

    GET/v1/locations
    curl -sS "$SZENERA_API/v1/locations" \  -H "Authorization: Bearer $SZENERA_API_KEY" \  | jq -r '.locations[] | "\(.id)  \(.name)"'
    loc_01KFB7QPW0LOCATION00000001  Filiale Nord
  2. Create the display. Name and location are enough. The idempotency key makes a retry after a dropped connection safe, so a second display never appears by accident:

    POST/v1/web-displays
    curl -sS -X POST "$SZENERA_API/v1/web-displays" \  -H "Authorization: Bearer $SZENERA_API_KEY" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: rollout-filiale-nord-theke" \  -d '{ "name": "Filiale Nord Theke", "locationId": "loc_01KFB7QPW0LOCATION00000001" }'
    Response
    {  "playerId": "ply_01KFB7QPW0PLAYER0000000001",  "token": "drc_01KFB7QPW0TOKEN00000000001",  "connectUrl": "https://webplayer.szenera.com/?reconnect=drc_01KFB7QPW0TOKEN00000000001",  "expiresAt": "2026-09-11T10:00:00.000Z"}

    The display exists at once. It occupies a booked seat and shows as Not connected in the fleet until a browser opens connectUrl.

  3. Assign the program. Open the program in the portal: the address ends with its ID sch_…. A scene (scene, sceneId), a playlist (playlist, playlistId), or a single media item (media, assetId) works the same way:

    PUT/v1/players/ply_01KFB7QPW0PLAYER0000000001/program
    curl -sS -X PUT "$SZENERA_API/v1/players/ply_01KFB7QPW0PLAYER0000000001/program" \  -H "Authorization: Bearer $SZENERA_API_KEY" \  -H "Content-Type: application/json" \  -d '{ "program": { "kind": "schedule", "scheduleId": "sch_01KFB7QPW0SCHEDULE0000001" } }'

    The order does not matter. A display that has a program before it is connected shows it as soon as the browser is there.

  4. Hand over the address. Send connectUrl to the person at the TV, for example by email. It works once and expires after 24 hours. Whoever opens it in the browser of the TV binds that browser to this display.

The whole rollout as a script

The script reads a CSV file with a header row and the columns name and location, creates one display per row, assigns the same program to all of them, and prints the name and address of each one:

For many identical displays at one location, POST /v1/web-displays/batch creates up to 50 in one call with numbered names, all or nothing. The routes for web displays carry x-stability: beta, see Web displays.

name,locationFiliale Nord Theke,Filiale NordFiliale Nord Schaufenster,Filiale NordFiliale Süd Theke,Filiale Süd
TypeScript
// rollout.ts: node rollout.ts displays.csvimport { readFile } from 'node:fs/promises'; const API = process.env.SZENERA_API;const KEY = process.env.SZENERA_API_KEY;const PROGRAM = process.env.SZENERA_PROGRAM_ID; // sch_… async function call(method: string, path: string, body?: unknown, idempotencyKey?: string) {  const headers: Record<string, string> = {    Authorization: `Bearer ${KEY}`,    'Content-Type': 'application/json',  };  if (idempotencyKey) headers['Idempotency-Key'] = idempotencyKey;  const res = await fetch(`${API}${path}`, {    method,    headers,    body: body === undefined ? undefined : JSON.stringify(body),  });  if (!res.ok) throw new Error(`${method} ${path}: ${res.status} ${await res.text()}`);  return res.json();} const { locations } = await call('GET', '/v1/locations');const locationIds = new Map(locations.map((location) => [location.name, location.id])); const rows = (await readFile(process.argv[2], 'utf8')).trim().split('\n').slice(1);for (const row of rows) {  const [name, locationName] = row.split(',').map((cell) => cell.trim());  const locationId = locationIds.get(locationName);  if (!locationId) throw new Error(`Unknown location: ${locationName}`);   // The same name gives the same key, so a second run within 24 hours creates nothing twice  const display = await call(    'POST',    '/v1/web-displays',    { name, locationId },    `rollout-${encodeURIComponent(name)}`,  );  await call('PUT', `/v1/players/${display.playerId}/program`, {    program: { kind: 'schedule', scheduleId: PROGRAM },  });  console.log(`${name}\t${display.connectUrl}`);}

Check the result

  • The display appears in the fleet with its name and location and the status Not connected.
  • GET /v1/players/{id}/program names the assigned program.
  • After the browser of the TV has opened the address, the status changes to Online and the program plays.

If something goes wrong

Symptom Meaning Solution
402 Billing.LicensesExhausted All booked seats are occupied. The response names booked and used. Book more seats or delete displays you no longer need.
403 Billing.FeatureNotEntitled The Displays without an agent capability is switched off for your organization. Ask the platform operator to enable it.
403 Permission.Denied The key lacks one of the three permissions. Create a key of the class Custom with exactly these permissions.
404 Location.NotFound The location ID does not belong to your organization. Take the ID from GET /v1/locations.
422 Schedule.ContentNotFound The program, scene, playlist, or media item does not exist. Check the ID in the portal address.
The address leads to a pairing code The address was already used or has expired. Issue a new one with POST /v1/web-displays/{id}/link and open that one.
3 min readUpdated Oct 30, 2026As Markdown

Was this helpful?