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.
- For
- Developers who set up displays from their own system
- Prerequisite
- a key
ak_…of the class Custom withfleet.web-display.manage,location.list, andschedule.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
-
Find the location.
GET /v1/locationsreturns every location of your organization in one response, without paging:GET/v1/locationscurl -sS "$SZENERA_API/v1/locations" \ -H "Authorization: Bearer $SZENERA_API_KEY" \ | jq -r '.locations[] | "\(.id) \(.name)"'loc_01KFB7QPW0LOCATION00000001 Filiale Nord -
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-displayscurl -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. -
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/programcurl -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.
-
Hand over the address. Send
connectUrlto 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// 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}/programnames 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. |
Was this helpful?