VISWOO DOCUMENTATION
Quickstart
First request, uploading media, building a playlist, scheduling and remote commands; curl, JavaScript and C# examples.
In the examples the key is in the VISWOO_KEY environment variable. First check that the key works:
curl https://api.viswoo.com/api/ext/v1/me -H "Authorization: Bearer $VISWOO_KEY"{ "tenant": { "id": "…", "name": "Corner Cafe" }, "key": { "id": "…", "name": "ERP", "scopes": ["screens:read"], "expiresAt": "2027-11-01T00:00:00Z" }}Listing screens
Section titled “Listing screens”Needs screens:read:
curl "https://api.viswoo.com/api/ext/v1/screens?online=false" -H "Authorization: Bearer $VISWOO_KEY"const api = (path, init = {}) => fetch(`https://api.viswoo.com/api/ext/v1${path}`, { ...init, headers: { Authorization: `Bearer ${process.env.VISWOO_KEY}`, 'Content-Type': 'application/json', ...init.headers }, }).then(async (r) => (r.ok ? (r.status === 204 ? null : r.json()) : Promise.reject(await r.json())));
const { items } = await api('/screens?pageSize=100');for (const screen of items) console.log(screen.name, screen.device?.online ? 'online' : 'offline');using System.Net.Http.Headers;using System.Net.Http.Json;using System.Text.Json;
var http = new HttpClient { BaseAddress = new Uri("https://api.viswoo.com/api/ext/v1/") };http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("VISWOO_KEY"));var page = await http.GetFromJsonAsync<JsonElement>("screens?pageSize=100");Uploading media
Section titled “Uploading media”Needs content:write. The file doesn’t go through the API; it takes three steps:
POST /media/uploadswith the file’s name, type and size returns a time-limited (30 min) upload address.PUTthe file to that address with the returnedheaders.- Call
POST /media/{id}/complete; Viswoo checks and processes the file. Once the status isreadyit can be used in a playlist (amedia.processedwebhook also arrives).
import { readFile, stat } from 'node:fs/promises';
const file = 'summer-sale.jpg';const { size } = await stat(file);const upload = await api('/media/uploads', { method: 'POST', body: JSON.stringify({ fileName: file, contentType: 'image/jpeg', sizeBytes: size }),});await fetch(upload.uploadUrl, { method: 'PUT', headers: upload.headers, body: await readFile(file) });await api(`/media/${upload.media.id}/complete`, { method: 'POST' });
let media;do { await new Promise((r) => setTimeout(r, 2000)); media = await api(`/media/${upload.media.id}`);} while (media.status === 'processing');Supported types, size limits and storage quota are the same as in the panel (media.unsupported_type,
media.too_large, media.storage_quota_exceeded).
Building a playlist
Section titled “Building a playlist”A playlist is created with its items in one request; an update writes the whole list too (items not in the list are removed).
curl -X POST https://api.viswoo.com/api/ext/v1/playlists \ -H "Authorization: Bearer $VISWOO_KEY" -H "Content-Type: application/json" \ -d '{ "name": "Summer sale", "shuffle": false, "items": [ { "mediaId": "MEDIA_ID", "durationSeconds": 8 }, { "mediaId": "MEDIA_ID_2", "durationSeconds": 8, "validFrom": "2026-06-01", "validTo": "2026-08-31" } ] }'Each item has exactly one of mediaId, designId, widget and pluginCode. When a playlist that is playing on
screens changes, the screens update within seconds.
Scheduling
Section titled “Scheduling”Rules are written as a whole; the first rule in the list has the highest priority. Try the effect before saving:
curl -X POST https://api.viswoo.com/api/ext/v1/schedule/impact \ -H "Authorization: Bearer $VISWOO_KEY" -H "Content-Type: application/json" \ -d '{ "rules": [ { "name": "Lunch menu", "playlistId": "PLAYLIST_ID", "days": [1,2,3,4,5], "start": "11:00", "end": "14:00" } ] }'The response says on how many screens the playing playlist changes in the next 7 days (affectedScreens,
changedHours) and when opening hours would be left empty (gaps). If it looks right, save the same body with
PUT /schedule. Days are 0 = Sunday … 6 = Saturday; if the end is before the start the rule crosses midnight. The
target is all, screens (screenIds) or tag (tag).
Remote commands
Section titled “Remote commands”Needs screens:write. The commands a screen accepts are in its device.availableCommands field:
curl -X POST https://api.viswoo.com/api/ext/v1/screens/SCREEN_ID/commands \ -H "Authorization: Bearer $VISWOO_KEY" -H "Content-Type: application/json" -d '{ "type": "refresh-manifest" }'The response is 202 with the command record. When the device answers, GET /screens/{id}/commands/{commandId}
shows succeeded or failed; if the device is offline the command becomes expired when its time runs out.
Emergency alert
Section titled “Emergency alert”curl -X POST https://api.viswoo.com/api/ext/v1/alerts \ -H "Authorization: Bearer $VISWOO_KEY" -H "Content-Type: application/json" \ -d '{ "title": "Evacuation drill", "message": "At 3 pm", "tone": "danger", "target": "all" }'The alert appears full screen on the chosen screens right away; without endsAt end it with
POST /alerts/{id}/end. It can be sent even while the account is suspended.