Learn about the API

Use the ScreenWay Studio REST API.

The public API is organized around account API keys and /api/v1 REST endpoints. Use it to automate screens, media, programs, apps, Designer projects, notifications, and analytics.

Authentication

Create an API key in Studio under Settings → API Keys. Keys start with swk_, are shown once, and can be scoped to an account, spaces, groups, or individual screens. Send every request with an Authorization: Bearer swk_… header.

list screens
curl https://studio.screenway.com/api/v1/screens \
  -H "Authorization: Bearer swk_your_api_key_here"

Key scopes

Each key carries a list of targets. Notifications and other target-aware endpoints honour those scopes: a request whose targets fall outside the key's allowed set is rejected with 403. All endpoints are account-scoped, so a key can never read or modify data in another account.

Target types

workspace
scope
Every screen in the account.
space
scope
All screens in the given space.
group
scope
All screens that are members of a screen group.
screen
scope
A single screen by ID.

Store keys securely

The full key is shown once on creation. Studio stores only a bcrypt hash and the first 12 characters as a lookup prefix. If a key leaks, revoke it immediately; there is no way to recover the original value.

Errors

All errors return a JSON body with a single error string and an appropriate HTTP status. Validation messages are human-readable and tell you what to fix.

error response
{
  "error": "Invalid command type. Allowed: content:program, content:media, system:restart, system:reload, playback:jump"
}
200OKRead or action succeeded.
201CreatedA new resource was created (uploads, folders, app instances, Designer projects).
400Bad RequestRequired field missing, value invalid, or body could not be parsed.
401UnauthorizedMissing or invalid Bearer token. Check the Authorization header.
403ForbiddenRequested targets are outside the API key scope (notifications and similar target-aware endpoints).
404Not FoundResource does not exist, or exists in a different account and is hidden from this key.
500Server ErrorUnexpected failure. Retry once, then check Studio status.

Cross-account lookups return 404

When a key tries to reach a resource that exists but belongs to another account, the API returns 404 instead of 403, to avoid leaking the existence of foreign IDs.

Pagination & responses

List endpoints return data plus meta. Pagination uses limit (default 50, maximum 200) and offset (default 0). Single-resource endpoints return a single data object without meta.

paginated request
GET /api/v1/screens?limit=100&offset=200

{
  "data": [ ... ],
  "meta": { "total": 412, "limit": 100, "offset": 200 }
}

Common query parameters

limit
integer
Page size. Default 50, capped at 200.
offset
integer
Number of items to skip. Use together with limit for paging.
space_id
uuid
Restrict results to a single space within the account.

Rate limits & retries

The v1 API does not enforce hard rate limits today, but the underlying Supabase and storage backends can throttle abusive traffic. Treat 500 responses as retryable with exponential backoff. Media uploads (POST /media, /from-url, /from-data) are the most expensive; keep concurrency low.

Idempotent reads

Safe to retry on any 5xx or network error.

Mutations

Retry only after confirming the previous call did not already succeed. None of the mutating endpoints currently support an idempotency key.

Bulk operations

Use the workflow endpoints (/programs/{id}/assign, notifications, app-instances) instead of looping over single calls.

Endpoint map

GET/api/v1/screensList screens, filter by space_id and status.
GET/api/v1/screens/{id}Read screen details and current state.
POST/api/v1/screens/{id}/commandsRestart, reload, jump playback, or push content.
GET/api/v1/mediaList account media.
POST/api/v1/mediaUpload media via multipart/form-data.
POST/api/v1/media/from-urlImport public media by URL.
POST/api/v1/media/from-dataUpload a data URL or base64 payload.
GET/api/v1/foldersList media folders for a space.
POST/api/v1/foldersCreate a media folder.
GET/api/v1/programsList account programs.
POST/api/v1/programs/{id}/assignAssign a program to one or more screens.
GET/api/v1/appsList ScreenWay apps available to the account.
GET/api/v1/app-instancesList configured app instances.
POST/api/v1/app-instancesCreate a configured app instance.
GET/api/v1/designer-projectsList Designer projects.
POST/api/v1/designer-projectsCreate a Designer project.
POST/api/v1/designer-projects/{id}/renderRender a Designer project to a PNG or JPEG data URL.
POST/api/v1/notificationsSend notifications to screens, spaces, groups, or account targets.
GET/api/v1/analyticsAccount analytics for a date range.

Screens

GET/api/v1/screens

List screens in the account, sorted by creation date.

Query parameters

space_id
uuid
Filter to a single space.
status
string
Filter by current player status (e.g. online, offline).
limit
integer
Default 50, max 200.
offset
integer
Default 0.
200 response
{
  "data": [
    {
      "id": "9e6e…",
      "name": "Lobby Display",
      "status": "online",
      "space_id": "a3b1…",
      "space_name": "Berlin HQ",
      "device_id": "dev_…",
      "is_paired": true,
      "paired_at": "2026-04-12T08:14:22Z",
      "last_seen_at": "2026-05-13T09:02:11Z",
      "player_version": "1.42.0",
      "os_version": "Android 13",
      "resolution": "1920x1080",
      "location": {
        "street_address": "Friedrichstr. 1",
        "postal_code": "10117",
        "city": "Berlin",
        "country": "DE"
      },
      "rotation": 0,
      "created_at": "2026-03-01T11:00:00Z",
      "updated_at": "2026-05-13T09:02:11Z"
    }
  ],
  "meta": { "total": 1, "limit": 50, "offset": 0 }
}
POST/api/v1/screens/{id}/commands

Send a command to a single screen. The command is persisted in screen_commands and broadcast via Supabase Realtime.

Body

typerequired
string
One of content:program, content:media, system:restart, system:reload, playback:jump.
payload.programId
uuid
Required when type=content:program.
payload.mediaId
uuid
Required when type=content:media.
example body
{
  "type": "content:program",
  "payload": { "programId": "prog_…" }
}
200 response
{
  "data": {
    "id": "cmd_…",
    "screen_id": "9e6e…",
    "type": "content:program",
    "status": "sent",
    "created_at": "2026-05-13T09:14:55Z"
  }
}

Media & folders

GET/api/v1/media

List media items in the account, newest first.

Query parameters

space_id
uuid
Limit to one space.
type
string
Filter by media type (image, video, …).
folder_id
uuid | "__root__"
Use __root__ for media without a folder.
limit / offset
integer
Standard pagination.
POST/api/v1/media/from-url

Import an image or video from a public HTTP(S) URL. Localhost and private network ranges are rejected.

Body

space_idrequired
uuid
Target space.
urlrequired
string
Public http(s) URL.
name
string
Display name; falls back to the remote filename.
folder_id
uuid
Existing folder to drop the file into.
folder_name
string
Create or reuse a folder with this name; combine with parent_folder_id.
parent_folder_id
uuid
Parent folder for folder_name lookups.
example body
{
  "space_id": "a3b1…",
  "url": "https://example.com/poster.png",
  "name": "Spring poster",
  "folder_name": "Posters"
}
POST/api/v1/media/from-data

Upload a data URL or base64 payload. Useful for AI-generated assets or QR codes.

Body

space_idrequired
uuid
Target space.
data_url
string
Either data_url or base64_data is required.
base64_data
string
Raw base64 string (use with mime_type).
mime_type
string
Required when only base64_data is given.
file_name
string
Filename hint for the upload.
example body
{
  "space_id": "a3b1…",
  "data_url": "data:image/png;base64,iVBORw0KGgo...",
  "file_name": "qr.png"
}

Programs

GET/api/v1/programs

List programs in the account.

Query parameters

space_id
uuid
Limit to one space.
limit / offset
integer
Standard pagination.
POST/api/v1/programs/{id}/assign

Assign a program to one or more screens. Stores the assignment and broadcasts a content:program command in one shot.

Body

screen_idsrequired
uuid[]
Non-empty list of screen IDs in the account.
example body
{
  "screen_ids": ["9e6e…", "12b3…"]
}

The response splits the input list into assigned_screen_ids and skipped_screen_ids. Invalid or foreign IDs are skipped silently.

Apps & app instances

GET/api/v1/apps

List active ScreenWay apps. Use the slug to find a specific one.

Query parameters

slug
string
Exact slug match (e.g. clock, google-sheets).
category
string
Filter by category (utility, info, entertainment, …).
search
string
Substring search over name, slug, description.
limit / offset
integer
Standard pagination.
POST/api/v1/app-instances

Create a configured app instance in a space.

Body

app_idrequired
uuid
The app to instantiate.
space_idrequired
uuid
Target space.
namerequired
string
Display name for the instance.
config
object | string
Configuration that satisfies the app config_schema. String values must be valid JSON.
transparent_background
boolean
Run the app over a transparent layer (overlay-friendly).

Designer projects

POST/api/v1/designer-projects/{id}/render

Render a Designer project's exported HTML to an image data URL. Optionally write the result back as the project thumbnail.

Body

format
"png" | "jpeg"
Defaults to PNG. JPEG honours quality.
width / height
integer
Override the rendered canvas size; defaults to the project canvas.
quality
integer (0-100)
JPEG quality.
wait_ms
integer
Extra delay before snapshotting (let fonts/animations settle).
update_thumbnail
boolean
When true, store the rendered data URL as the project thumbnail.
base_url
string
Override the base URL used to resolve relative assets. Defaults to the request host.
example body
{
  "format": "png",
  "width": 1920,
  "height": 1080,
  "update_thumbnail": false
}

Render requires exported_html

Projects without exported_html respond with 400. Save the project in Designer once to generate it.

Notifications

POST/api/v1/notifications

Broadcast a notification overlay to screens. Omitting targets uses the API key's configured target list.

Body

titlerequired
string
Overlay title.
messagerequired
string
Body text.
categoryrequired
enum
INFO, SUCCESS, WARNING, ALERT, EMERGENCY, or PROMO.
priorityrequired
enum
LOW, NORMAL, HIGH, or CRITICAL.
icon
string
Optional icon hint (player-defined).
action_url
string
Optional CTA URL.
action_label
string
Optional CTA label.
duration_seconds
number
How long the overlay should stay on screen.
targets
Target[]
Override the API key targets. Must be a subset, otherwise 403.
example body
{
  "title": "Service desk",
  "message": "Counter 4 is now open",
  "category": "INFO",
  "priority": "NORMAL",
  "duration_seconds": 30,
  "action_url": "https://studio.screenway.com/dashboard",
  "action_label": "Open dashboard"
}
200 response
{
  "data": {
    "id": "0a7e…",
    "screens_notified": 4,
    "targets": [{ "type": "workspace", "id": "ws_…" }],
    "resolved_screen_ids": ["9e6e…", "12b3…", "44dd…", "8f1c…"]
  }
}

Analytics

GET/api/v1/analytics

Account-wide analytics rollup. Defaults to the last 7 days.

Query parameters

from
date (YYYY-MM-DD)
Start of the window. Defaults to 7 days ago.
to
date (YYYY-MM-DD)
End of the window. Defaults to today.
space_id
uuid
Limit to one space.

The response includes screen counts (total / online / offline), average uptime, total items played, a per-screen breakdown, and the top 10 played items.

Common workflows

Media automation

Import files via /media/from-url, organize them with folders, then push them onto screens with /screens/{id}/commands (type=content:media).

Playback control

Build a playlist in Studio, then call /programs/{id}/assign to switch many screens at once instead of sending individual commands.

Designer pipeline

Create or update a Designer project, render a PNG with update_thumbnail=true, then assign the project to a program slot in Studio.

Notifications

Push time-sensitive overlays to the key's default targets or pass targets for a finer scope.

Reporting

Pull /analytics on a schedule into your BI tool, then group by space with space_id.

Key hygiene

Use one key per integration. Restrict targets to the screens that integration is allowed to touch.

Account scoping

Every endpoint resolves data through the key's account. There is no need (and no way) to pass a workspace_id manually.

Designer + Apps

Combine /app-instances and /designer-projects to assemble layered, scheduled content without ever opening Studio.

Render-then-render

Re-render a Designer project after each automated edit so the program preview and store assets stay in sync.