Skip to content
developers
Developer home

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:

Terminal window
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" }
}

Needs screens:read:

Terminal window
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");

Needs content:write. The file doesn’t go through the API; it takes three steps:

  1. POST /media/uploads with the file’s name, type and size returns a time-limited (30 min) upload address.
  2. PUT the file to that address with the returned headers.
  3. Call POST /media/{id}/complete; Viswoo checks and processes the file. Once the status is ready it can be used in a playlist (a media.processed webhook 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).

A playlist is created with its items in one request; an update writes the whole list too (items not in the list are removed).

Terminal window
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.

Rules are written as a whole; the first rule in the list has the highest priority. Try the effect before saving:

Terminal window
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).

Needs screens:write. The commands a screen accepts are in its device.availableCommands field:

Terminal window
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.

Terminal window
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.