SignBlink API

Manage screens, playlists, media and emergency alerts from your own systems: update a menu from your till, send alerts from a safety system, or watch for screens that go offline. Free with every SignBlink account.

https://app.signblink.com/api/v1 Get an API key OpenAPI file

Quick start

  1. Create a key. In SignBlink, go to Settings → API and choose Create a key. Only account owners can.
  2. Keep it secret. Store it where your code runs, for example in an environment variable called SIGNBLINK_KEY.
  3. Make a call. This answers with your account’s name:
curl https://app.signblink.com/api/v1/me \
  -H "Authorization: Bearer $SIGNBLINK_KEY"

Every address in this guide starts with https://app.signblink.com/api/v1.

Authentication

Send your key with every request, in an Authorization header: Authorization: Bearer sb_…. A key belongs to one SignBlink account and can only see and change that account.

  • Full access keys can do everything here: change playlists, upload, publish, restart players and send alerts.
  • Read only keys can look but not change anything. Use them for status boards and monitoring.

An account can have up to 20 keys. Make one for each system that connects, so you can see what each one did (Settings → Activity shows actions as “API key ‘name’”) and delete one without touching the rest. A deleted key stops working immediately.

Keys are secret. Use them from your server, not in a web page or app that other people can open. The API does answer requests from browsers, but anyone who can see a key can use it.

Requests and responses

  • Send JSON bodies with Content-Type: application/json. Uploads can also be multipart/form-data.
  • Responses are JSON, wrapped in a named object, like {"screen": {…}} or {"screens": […]}.
  • Times are ISO 8601 in UTC, like 2026-10-24T14:05:00.000Z. Playlist item from and until are the screen’s local time, like 2026-11-01T06:00.
  • Ids are strings with a prefix that says what they are: scr_ screens, pl_ playlists, med_ files, dsg_ designs, grp_ groups, alr_ alerts.
  • File URLs in responses are long, unguessable addresses anyone holding them can open; they change when a file is replaced.

Errors

Anything other than a 2xx status comes with an error object. message is written for people, so it’s fine to show it.

{
  "error": {
    "code": "bad_request",
    "message": "Add a headline for the alert.",
    "field": "title"
  }
}
StatusCodeWhat it means
400bad_requestSomething in the request isn’t right; message says what, and field which field.
401unauthorizedNo key, a malformed key, or a deleted one.
402limit_reachedThe account is over a plan limit (storage, screens), or paused for billing.
403forbiddenA read-only key tried to change something, or the account is closed.
404not_foundNo such screen, playlist, file or alert in this account (or no such endpoint).
413too_largeThe uploaded file is over the size limit.
429rate_limitedToo many requests. Wait a few seconds and try again.
500server_errorSomething went wrong on our side. Try again; if it keeps happening, tell us.
507server_fullOur storage is temporarily full. Try the upload again later.

Rate limits

Each key can make 120 requests in a burst, refilling at 120 a minute (two a second). Past that you get 429; wait a moment and retry. A few actions have their own limits: alerts (10 a minute per account), restarts (3 a minute per screen), screenshots (6 a minute per screen) and uploads (30 a minute per key).

To watch screens, ask for the list every minute or so. Screens report in about every 30 seconds, so polling faster doesn’t tell you more.

Drafts and publishing

Like the dashboard, changes to playlists and screens are drafts until they’re published, so you can make several changes and send them together. Publishing sends the latest version to the screens, which switch over within a few seconds.

  • Add "publish": true to a change to publish the affected screens straight away.
  • Or call Publish when you’re done, for one screen, a group or the whole account.
  • A screen’s unpublishedChanges says whether it has drafts waiting.

Alerts are different: they go up on screen immediately, with no publishing.

Webhooks

Rather than asking every minute, have SignBlink tell you. Add an https address in Settings → API → Webhooks, or with Create a webhook, and SignBlink POSTs a JSON event to it when something happens:

EventWhen
screen.offlineA connected screen stopped checking in for a few minutes.
screen.onlineA screen that went offline is checking in normally again.
screens.publishedSomeone (or an API key) published changes to screens.
alert.sentAn emergency alert or announcement went up, including automatic weather warnings.
alert.endedAn alert came down, by hand or at its end time.
What you receive
{
  "id": "evt_Xy7Zt3Kp1Lm9Qr2Sd5",
  "type": "screen.offline",
  "createdAt": "2026-10-24T14:05:00.000Z",
  "account": {
    "id": "org_Ab12Cd34Ef56Gh78Ij",
    "name": "Maple Street Café"
  },
  "data": {
    "screen": {
      "id": "scr_8Kq2mXa1bC3dE4fG5h",
      "name": "Front counter",
      "location": "Main Street"
    },
    "lastSeenAt": "2026-10-24T13:59:41.000Z"
  }
}

Answer with any 2xx within 10 seconds. Anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 8 hours. An address that fails 10 events in a row is paused (resume it in Settings → API). The same event can arrive twice; use its id to ignore repeats.

Check it came from SignBlink

Every delivery has a SignBlink-Signature header: t=<unix seconds>,v1=<signature>. The signature is an HMAC-SHA256, made with your webhook’s secret, of the timestamp, a dot, and the raw request body. Compute it yourself and compare; reject deliveries more than five minutes old.

import crypto from 'node:crypto';

// rawBody: the request body exactly as received (a string, before JSON.parse).
function fromSignBlink(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
  const expected = crypto.createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 || ''));
}

Using Zapier or Make? Their “catch hook” addresses work as webhooks, and their HTTP actions can call this API. See Connect SignBlink to Zapier or Make for step-by-step recipes.

Recipes

Keep a menu playlist in step with another system

Upload today’s pictures by address, then replace the playlist’s items and publish in one call.

const api = (path, init = {}) =>
  fetch(`https://app.signblink.com/api/v1${path}`, {
    ...init,
    headers: { Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`, 'Content-Type': 'application/json' },
  }).then((r) => r.json());

const PLAYLIST = 'pl_Lm4nOp5qRs6tUv7wXy';
const photos = ['https://example.com/menu/soup.jpg', 'https://example.com/menu/salad.jpg'];

const items = [];
for (const url of photos) {
  const { media } = await api('/media', { method: 'POST', body: JSON.stringify({ url }) });
  items.push({ type: 'media', refId: media.id, durationSec: 10 });
}
// Photos are ready in a few seconds; screens show them once they are.
await api(`/playlists/${PLAYLIST}`, { method: 'PUT', body: JSON.stringify({ items, publish: true }) });

Send an alert from a safety system

Put an emergency message on every screen, then take it down when the all-clear comes.

const headers = { Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`, 'Content-Type': 'application/json' };

const { alert } = await fetch('https://app.signblink.com/api/v1/alerts', {
  method: 'POST',
  headers,
  body: JSON.stringify({ style: 'emergency', title: 'Lockdown', body: 'Stay in your room. Lock the door.', allScreens: true, sound: true }),
}).then((r) => r.json());

// Later, when it's over:
await fetch(`https://app.signblink.com/api/v1/alerts/${alert.id}/end`, { method: 'POST', headers });

Get told when a screen goes offline

The simplest way is a webhook for screen.offline (see Webhooks above). Or check every few minutes with a read-only key, and raise your own alarm for screens that stopped reporting.

const { screens } = await fetch('https://app.signblink.com/api/v1/screens', {
  headers: { Authorization: `Bearer ${process.env.SIGNBLINK_KEY}` },
}).then((r) => r.json());

const down = screens.filter((s) => s.status === 'offline');
for (const s of down) console.log(`${s.name} last reported ${s.lastSeenAt}`);

Versions and OpenAPI

This is version 1. We may add endpoints and fields at any time, so ignore fields you don’t know. We won’t rename or remove anything in version 1; changes like that would come as /v2, with plenty of notice.

A machine-readable description is at https://app.signblink.com/api/v1/openapi.json (OpenAPI 3.1). Import it into Postman, Insomnia or a code generator.

Account

Which account a key belongs to, and its limits.

Check your key

GET/v1/meAny key

Which account and key you’re using, plus the account’s screen and storage limits. A good first call.

curl https://app.signblink.com/api/v1/me \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response
{
  "account": {
    "id": "org_Fq3Wd5Tg7Yh9Jk1Lz2",
    "name": "Maple Street Cafe",
    "timezone": "America/New_York"
  },
  "key": {
    "id": "key_N4bV6cX8zL0kJ2",
    "name": "Menu sync",
    "access": "write"
  },
  "limits": {
    "screens": 5,
    "storageBytes": 6442450944,
    "storageUsedBytes": 1288490188
  }
}

Screens

Your screens: their status, what they play, restarts and screenshots.

List screens

GET/v1/screensAny key

Every screen in the account with its live status. status is online, offline, updating (still receiving a publish) or not_connected (no player paired yet). problems lists anything that needs attention, in plain English.

Parameters

groupstring · query
Only screens in this group (a group id).
curl https://app.signblink.com/api/v1/screens \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response
{
  "screens": [
    {
      "id": "scr_8Kq2mXa1bC3dE4fG5h",
      "name": "Front counter",
      "location": "Main Street",
      "orientation": "landscape",
      "timezone": "America/New_York",
      "status": "online",
      "online": true,
      "lastSeenAt": "2026-10-24T14:05:00.000Z",
      "playlistId": "pl_Lm4nOp5qRs6tUv7wXy",
      "scheduled": false,
      "unpublishedChanges": false,
      "publishedAt": "2026-10-24T13:40:12.000Z",
      "nowPlaying": {
        "name": "Fall menu",
        "since": "2026-10-24T14:04:41.000Z"
      },
      "player": {
        "version": "1.10.0",
        "app": {
          "name": "SignBlink TV",
          "version": "1.0.0"
        },
        "resolution": "1920x1080"
      },
      "groups": [
        {
          "id": "grp_Ab12Cd34Ef56Gh78Ij",
          "name": "Downtown"
        }
      ],
      "screenshot": {
        "takenAt": "2026-10-24T13:58:02.000Z",
        "url": "https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h/screenshot"
      },
      "problems": [],
      "createdAt": "2026-10-02T16:21:09.000Z"
    }
  ]
}

Get a screen

GET/v1/screens/{id}Any key

One screen and its live status.

Parameters

idstring · in the pathrequired
The screen’s id.
curl https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response
{
  "screen": {
    "id": "scr_8Kq2mXa1bC3dE4fG5h",
    "name": "Front counter",
    "location": "Main Street",
    "orientation": "landscape",
    "timezone": "America/New_York",
    "status": "online",
    "online": true,
    "lastSeenAt": "2026-10-24T14:05:00.000Z",
    "playlistId": "pl_Lm4nOp5qRs6tUv7wXy",
    "scheduled": false,
    "unpublishedChanges": false,
    "publishedAt": "2026-10-24T13:40:12.000Z",
    "nowPlaying": {
      "name": "Fall menu",
      "since": "2026-10-24T14:04:41.000Z"
    },
    "player": {
      "version": "1.10.0",
      "app": {
        "name": "SignBlink TV",
        "version": "1.0.0"
      },
      "resolution": "1920x1080"
    },
    "groups": [
      {
        "id": "grp_Ab12Cd34Ef56Gh78Ij",
        "name": "Downtown"
      }
    ],
    "screenshot": {
      "takenAt": "2026-10-24T13:58:02.000Z",
      "url": "https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h/screenshot"
    },
    "problems": [],
    "createdAt": "2026-10-02T16:21:09.000Z"
  }
}

Update a screen

PATCH/v1/screens/{id}Full-access key

Rename a screen, or change which playlist it plays. Changing the playlist is a draft until published; send "publish": true to put it on screen now.

Parameters

idstring · in the pathrequired
The screen’s id.

Body

namestring
A new name (up to 80 characters).
locationstring
Where it is, e.g. “Main Street” (up to 60 characters).
playlistIdstring
The playlist this screen plays (when no schedule says otherwise).
publishboolean
Publish this screen right after the change. Default false.
curl -X PATCH https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h \
  -H "Authorization: Bearer $SIGNBLINK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"playlistId":"pl_Lm4nOp5qRs6tUv7wXy","publish":true}'
Response
{
  "screen": {
    "id": "scr_8Kq2mXa1bC3dE4fG5h",
    "name": "Front counter",
    "location": "Main Street",
    "orientation": "landscape",
    "timezone": "America/New_York",
    "status": "online",
    "online": true,
    "lastSeenAt": "2026-10-24T14:05:00.000Z",
    "playlistId": "pl_Lm4nOp5qRs6tUv7wXy",
    "scheduled": false,
    "unpublishedChanges": false,
    "publishedAt": "2026-10-24T13:40:12.000Z",
    "nowPlaying": {
      "name": "Fall menu",
      "since": "2026-10-24T14:04:41.000Z"
    },
    "player": {
      "version": "1.10.0",
      "app": {
        "name": "SignBlink TV",
        "version": "1.0.0"
      },
      "resolution": "1920x1080"
    },
    "groups": [
      {
        "id": "grp_Ab12Cd34Ef56Gh78Ij",
        "name": "Downtown"
      }
    ],
    "screenshot": {
      "takenAt": "2026-10-24T13:58:02.000Z",
      "url": "https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h/screenshot"
    },
    "problems": [],
    "createdAt": "2026-10-02T16:21:09.000Z"
  },
  "published": [
    {
      "screenId": "scr_8Kq2mXa1bC3dE4fG5h",
      "name": "Front counter",
      "version": 14,
      "changed": true,
      "warnings": [],
      "blocked": null
    }
  ]
}

Restart the player

POST/v1/screens/{id}/restartFull-access key

Reloads the player on the screen, like Restart player in the dashboard. It’s back within a few seconds. Up to 3 restarts a minute per screen.

Parameters

idstring · in the pathrequired
The screen’s id.
curl -X POST https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h/restart \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response
{
  "ok": true
}

Take a screenshot

POST/v1/screens/{id}/screenshotFull-access key

Asks the player for a picture of what it’s showing. It usually arrives within 5 seconds: poll the screen until screenshot.takenAt is later than requestedAt, then download it.

Parameters

idstring · in the pathrequired
The screen’s id.
curl -X POST https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h/screenshot \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response
{
  "requestedAt": "2026-10-24T14:05:00.000Z",
  "url": "https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h/screenshot"
}

Download the latest screenshot

GET/v1/screens/{id}/screenshotAny key

The most recent screenshot, as a JPEG. 404 when none has been taken yet.

Parameters

idstring · in the pathrequired
The screen’s id.
curl https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h/screenshot \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response · image/jpeg
(the image file)

Groups

Screen groups, for acting on several screens at once.

List groups

GET/v1/groupsAny key

Screen groups and the screens in each. Use a group id to list its screens or publish to all of them.

curl https://app.signblink.com/api/v1/groups \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response
{
  "groups": [
    {
      "id": "grp_Ab12Cd34Ef56Gh78Ij",
      "name": "Downtown",
      "screenIds": [
        "scr_8Kq2mXa1bC3dE4fG5h"
      ]
    }
  ]
}

Playlists

What screens play, in order. Change items, then publish.

List playlists

GET/v1/playlistsAny key

Every playlist, with the screens that play it. Items are left out here; get one playlist for those.

curl https://app.signblink.com/api/v1/playlists \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response
{
  "playlists": [
    {
      "id": "pl_Lm4nOp5qRs6tUv7wXy",
      "name": "Fall menu",
      "itemCount": 2,
      "screens": [
        {
          "id": "scr_8Kq2mXa1bC3dE4fG5h",
          "name": "Front counter"
        }
      ],
      "publishedAt": "2026-10-24T13:40:12.000Z",
      "updatedAt": "2026-10-24T13:39:55.000Z"
    }
  ]
}

Get a playlist

GET/v1/playlists/{id}Any key

A playlist with its items in order. missing: true marks an item whose file or design was deleted.

Parameters

idstring · in the pathrequired
The playlist’s id.
curl https://app.signblink.com/api/v1/playlists/pl_Lm4nOp5qRs6tUv7wXy \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response
{
  "playlist": {
    "id": "pl_Lm4nOp5qRs6tUv7wXy",
    "name": "Fall menu",
    "itemCount": 2,
    "screens": [
      {
        "id": "scr_8Kq2mXa1bC3dE4fG5h",
        "name": "Front counter"
      }
    ],
    "publishedAt": "2026-10-24T13:40:12.000Z",
    "updatedAt": "2026-10-24T13:39:55.000Z",
    "items": [
      {
        "id": "it_Q1w2E3r4T5",
        "type": "media",
        "refId": "med_Zx9Cv8Bn7Mm6Ll5Kk4",
        "durationSec": 10,
        "fit": "cover",
        "from": null,
        "until": null,
        "enabled": true,
        "name": "pumpkin-latte.jpg",
        "kind": "image",
        "missing": false
      },
      {
        "id": "it_Y6u7I8o9P0",
        "type": "design",
        "refId": "dsg_Hg5Fd4Sa3Qw2Er1Ty0",
        "durationSec": 15,
        "fit": null,
        "from": null,
        "until": null,
        "enabled": true,
        "name": "Today’s specials",
        "kind": "design",
        "missing": false
      }
    ]
  }
}

Create a playlist

POST/v1/playlistsFull-access key

A new playlist. Point a screen at it with Update a screen.

Body

namestringrequired
Up to 80 characters.
itemsarray
Items to start with, in order (see Add an item for their fields). Up to 200.
curl -X POST https://app.signblink.com/api/v1/playlists \
  -H "Authorization: Bearer $SIGNBLINK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Lunch specials","items":[{"type":"media","refId":"med_Zx9Cv8Bn7Mm6Ll5Kk4","durationSec":8}]}'
Response
{
  "playlist": {
    "id": "pl_Lm4nOp5qRs6tUv7wXy",
    "name": "Fall menu",
    "itemCount": 2,
    "screens": [
      {
        "id": "scr_8Kq2mXa1bC3dE4fG5h",
        "name": "Front counter"
      }
    ],
    "publishedAt": "2026-10-24T13:40:12.000Z",
    "updatedAt": "2026-10-24T13:39:55.000Z",
    "items": [
      {
        "id": "it_Q1w2E3r4T5",
        "type": "media",
        "refId": "med_Zx9Cv8Bn7Mm6Ll5Kk4",
        "durationSec": 10,
        "fit": "cover",
        "from": null,
        "until": null,
        "enabled": true,
        "name": "pumpkin-latte.jpg",
        "kind": "image",
        "missing": false
      },
      {
        "id": "it_Y6u7I8o9P0",
        "type": "design",
        "refId": "dsg_Hg5Fd4Sa3Qw2Er1Ty0",
        "durationSec": 15,
        "fit": null,
        "from": null,
        "until": null,
        "enabled": true,
        "name": "Today’s specials",
        "kind": "design",
        "missing": false
      }
    ]
  }
}

Replace a playlist’s items

PUT/v1/playlists/{id}Full-access key

Renames a playlist and/or replaces all its items at once: the simplest way to keep a playlist in step with another system. Items you send without an id get a new one.

Parameters

idstring · in the pathrequired
The playlist’s id.

Body

namestring
A new name.
itemsarray
The complete list of items, in order (up to 200).
publishboolean
Also publish to the screens that play this playlist, so the change shows straight away. Default false: the change waits as a draft.
curl -X PUT https://app.signblink.com/api/v1/playlists/pl_Lm4nOp5qRs6tUv7wXy \
  -H "Authorization: Bearer $SIGNBLINK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"items":[{"type":"media","refId":"med_Zx9Cv8Bn7Mm6Ll5Kk4","durationSec":10},{"type":"design","refId":"dsg_Hg5Fd4Sa3Qw2Er1Ty0","durationSec":15}],"publish":true}'
Response
{
  "playlist": {
    "id": "pl_Lm4nOp5qRs6tUv7wXy",
    "name": "Fall menu",
    "itemCount": 2,
    "screens": [
      {
        "id": "scr_8Kq2mXa1bC3dE4fG5h",
        "name": "Front counter"
      }
    ],
    "publishedAt": "2026-10-24T13:40:12.000Z",
    "updatedAt": "2026-10-24T13:39:55.000Z",
    "items": [
      {
        "id": "it_Q1w2E3r4T5",
        "type": "media",
        "refId": "med_Zx9Cv8Bn7Mm6Ll5Kk4",
        "durationSec": 10,
        "fit": "cover",
        "from": null,
        "until": null,
        "enabled": true,
        "name": "pumpkin-latte.jpg",
        "kind": "image",
        "missing": false
      },
      {
        "id": "it_Y6u7I8o9P0",
        "type": "design",
        "refId": "dsg_Hg5Fd4Sa3Qw2Er1Ty0",
        "durationSec": 15,
        "fit": null,
        "from": null,
        "until": null,
        "enabled": true,
        "name": "Today’s specials",
        "kind": "design",
        "missing": false
      }
    ]
  },
  "published": [
    {
      "screenId": "scr_8Kq2mXa1bC3dE4fG5h",
      "name": "Front counter",
      "version": 14,
      "changed": true,
      "warnings": [],
      "blocked": null
    }
  ]
}

Add an item

POST/v1/playlists/{id}/itemsFull-access key

Adds one photo, video, PDF or design to the end (or start) of a playlist.

Parameters

idstring · in the pathrequired
The playlist’s id.

Body

typestringrequired
A file from your library, or a design. One of media, design.
refIdstringrequired
The media or design id.
durationSecinteger
Seconds on screen (2–3600). Photos default to 10, designs to 12; videos play to the end.
fitstring
Photos and videos: show all of it (contain) or fill the screen (cover). One of contain, cover.
fromstring
Only show from this local date and time, like 2026-11-01T06:00.
untilstring
Stop showing at this local date and time.
enabledboolean
false keeps it in the playlist without showing it.
positionstring
Where to add it. Default end. One of start, end.
publishboolean
Also publish to the screens that play this playlist, so the change shows straight away. Default false: the change waits as a draft.
curl -X POST https://app.signblink.com/api/v1/playlists/pl_Lm4nOp5qRs6tUv7wXy/items \
  -H "Authorization: Bearer $SIGNBLINK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type":"media","refId":"med_Zx9Cv8Bn7Mm6Ll5Kk4","durationSec":10,"publish":true}'
Response
{
  "item": {
    "id": "it_Q1w2E3r4T5",
    "type": "media",
    "refId": "med_Zx9Cv8Bn7Mm6Ll5Kk4",
    "durationSec": 10,
    "fit": "cover",
    "from": null,
    "until": null,
    "enabled": true,
    "name": "pumpkin-latte.jpg",
    "kind": "image",
    "missing": false
  },
  "playlist": {
    "id": "pl_Lm4nOp5qRs6tUv7wXy",
    "name": "Fall menu",
    "itemCount": 2,
    "screens": [
      {
        "id": "scr_8Kq2mXa1bC3dE4fG5h",
        "name": "Front counter"
      }
    ],
    "publishedAt": "2026-10-24T13:40:12.000Z",
    "updatedAt": "2026-10-24T13:39:55.000Z",
    "items": [
      {
        "id": "it_Q1w2E3r4T5",
        "type": "media",
        "refId": "med_Zx9Cv8Bn7Mm6Ll5Kk4",
        "durationSec": 10,
        "fit": "cover",
        "from": null,
        "until": null,
        "enabled": true,
        "name": "pumpkin-latte.jpg",
        "kind": "image",
        "missing": false
      },
      {
        "id": "it_Y6u7I8o9P0",
        "type": "design",
        "refId": "dsg_Hg5Fd4Sa3Qw2Er1Ty0",
        "durationSec": 15,
        "fit": null,
        "from": null,
        "until": null,
        "enabled": true,
        "name": "Today’s specials",
        "kind": "design",
        "missing": false
      }
    ]
  },
  "published": [
    {
      "screenId": "scr_8Kq2mXa1bC3dE4fG5h",
      "name": "Front counter",
      "version": 14,
      "changed": true,
      "warnings": [],
      "blocked": null
    }
  ]
}

Remove an item

DELETE/v1/playlists/{id}/items/{itemId}Full-access key

Takes one item out of a playlist (the file or design itself stays in your library). Add ?publish=true to publish straight away.

Parameters

idstring · in the pathrequired
The playlist’s id.
itemIdstring · in the pathrequired
The item’s id (from Get a playlist).
publishboolean · query
Publish to the screens that play this playlist.
curl -X DELETE https://app.signblink.com/api/v1/playlists/pl_Lm4nOp5qRs6tUv7wXy/items/it_Q1w2E3r4T5 \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response
{
  "playlist": {
    "id": "pl_Lm4nOp5qRs6tUv7wXy",
    "name": "Fall menu",
    "itemCount": 2,
    "screens": [
      {
        "id": "scr_8Kq2mXa1bC3dE4fG5h",
        "name": "Front counter"
      }
    ],
    "publishedAt": "2026-10-24T13:40:12.000Z",
    "updatedAt": "2026-10-24T13:39:55.000Z",
    "items": [
      {
        "id": "it_Q1w2E3r4T5",
        "type": "media",
        "refId": "med_Zx9Cv8Bn7Mm6Ll5Kk4",
        "durationSec": 10,
        "fit": "cover",
        "from": null,
        "until": null,
        "enabled": true,
        "name": "pumpkin-latte.jpg",
        "kind": "image",
        "missing": false
      },
      {
        "id": "it_Y6u7I8o9P0",
        "type": "design",
        "refId": "dsg_Hg5Fd4Sa3Qw2Er1Ty0",
        "durationSec": 15,
        "fit": null,
        "from": null,
        "until": null,
        "enabled": true,
        "name": "Today’s specials",
        "kind": "design",
        "missing": false
      }
    ]
  }
}

Media

Photos, videos, PDFs, presentations and audio in your library. Upload files or have SignBlink fetch them.

List media

GET/v1/mediaAny key

Files in your library, newest first. status is processing for a minute or two after an upload (videos are converted for smooth playback), then ready, or failed with an error.

Parameters

kindstring · query
Only this kind of file. One of image, video, pdf, audio.
folderIdstring · query
Only files in this folder.
curl https://app.signblink.com/api/v1/media \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response
{
  "media": [
    {
      "id": "med_Zx9Cv8Bn7Mm6Ll5Kk4",
      "name": "pumpkin-latte.jpg",
      "kind": "image",
      "status": "ready",
      "error": null,
      "folderId": null,
      "bytes": 482113,
      "width": 1920,
      "height": 1080,
      "durationMs": null,
      "pages": null,
      "url": "https://app.signblink.com/m/f3a9c1e0b7d24c6e8f0a1b2c3d4e5f60.webp",
      "thumbnailUrl": "https://app.signblink.com/m/f9b8a7c6d5e4f3a2b1c0d9e8f7a6b5c4.webp",
      "createdAt": "2026-10-20T09:12:44.000Z",
      "updatedAt": "2026-10-20T09:12:51.000Z"
    }
  ]
}

Get a file

GET/v1/media/{id}Any key

One file. Poll it after an upload until status is ready.

Parameters

idstring · in the pathrequired
The file’s id.
curl https://app.signblink.com/api/v1/media/med_Zx9Cv8Bn7Mm6Ll5Kk4 \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response
{
  "media": {
    "id": "med_Zx9Cv8Bn7Mm6Ll5Kk4",
    "name": "pumpkin-latte.jpg",
    "kind": "image",
    "status": "ready",
    "error": null,
    "folderId": null,
    "bytes": 482113,
    "width": 1920,
    "height": 1080,
    "durationMs": null,
    "pages": null,
    "url": "https://app.signblink.com/m/f3a9c1e0b7d24c6e8f0a1b2c3d4e5f60.webp",
    "thumbnailUrl": "https://app.signblink.com/m/f9b8a7c6d5e4f3a2b1c0d9e8f7a6b5c4.webp",
    "createdAt": "2026-10-20T09:12:44.000Z",
    "updatedAt": "2026-10-20T09:12:51.000Z"
  }
}

Upload a file

POST/v1/mediaFull-access key

Adds a photo, video, PDF, PowerPoint or audio file to the library. Either send the file itself as multipart/form-data (field file, up to 100 MB), or send JSON with a url and SignBlink downloads it (up to your plan’s file size limits). It counts toward your storage like any upload.

Parameters

folderIdstring · query
Multipart uploads: put it in this folder.

Body

filefile
Multipart: the file.
urlstring
JSON: a public http(s) address to download the file from.
namestring
The name to show in the library. Default: the file name.
folderIdstring
JSON: put it in this folder.

Send either the multipart file or the JSON body, not both.

# Send a file
curl -X POST https://app.signblink.com/api/v1/media \
  -H "Authorization: Bearer $SIGNBLINK_KEY" \
  -F "[email protected]"

# Or have SignBlink download one
curl -X POST https://app.signblink.com/api/v1/media \
  -H "Authorization: Bearer $SIGNBLINK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/menu/pumpkin-latte.jpg","name":"Pumpkin latte"}'
Response
{
  "media": {
    "id": "med_Zx9Cv8Bn7Mm6Ll5Kk4",
    "name": "pumpkin-latte.jpg",
    "kind": "image",
    "status": "processing",
    "error": null,
    "folderId": null,
    "bytes": 482113,
    "width": 1920,
    "height": 1080,
    "durationMs": null,
    "pages": null,
    "url": null,
    "thumbnailUrl": "https://app.signblink.com/m/f9b8a7c6d5e4f3a2b1c0d9e8f7a6b5c4.webp",
    "createdAt": "2026-10-20T09:12:44.000Z",
    "updatedAt": "2026-10-20T09:12:51.000Z"
  }
}

Delete a file

DELETE/v1/media/{id}Full-access key

Moves a file to the trash (it can be restored from the dashboard for 30 days). Playlists that use it skip it after the next publish.

Parameters

idstring · in the pathrequired
The file’s id.
curl -X DELETE https://app.signblink.com/api/v1/media/med_Zx9Cv8Bn7Mm6Ll5Kk4 \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response
{
  "ok": true
}

Designs

Designs made in the SignBlink designer, to add to playlists.

List designs

GET/v1/designsAny key

Designs in the account, most recently edited first. Add one to a playlist with "type": "design".

curl https://app.signblink.com/api/v1/designs \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response
{
  "designs": [
    {
      "id": "dsg_Hg5Fd4Sa3Qw2Er1Ty0",
      "name": "Today’s specials",
      "orientation": "landscape",
      "createdAt": "2026-10-11T10:02:19.000Z",
      "updatedAt": "2026-10-23T17:45:03.000Z"
    }
  ]
}

Publishing

Send your changes to screens.

Publish

POST/v1/publishFull-access key

Sends the latest playlists, designs and settings to screens. Screens with nothing new report changed: false. With no body, every screen in the account is published.

Body

screenIdsarray
Only these screens.
groupIdstring
Only the screens in this group.
curl -X POST https://app.signblink.com/api/v1/publish \
  -H "Authorization: Bearer $SIGNBLINK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"groupId":"grp_Ab12Cd34Ef56Gh78Ij"}'
Response
{
  "results": [
    {
      "screenId": "scr_8Kq2mXa1bC3dE4fG5h",
      "name": "Front counter",
      "version": 14,
      "changed": true,
      "warnings": [],
      "blocked": null
    }
  ]
}

Alerts

Emergency alerts and announcements that take over screens straight away.

List alerts

GET/v1/alertsAny key

The 50 most recent alerts. showing says whether each screen’s player confirmed it has the alert on screen.

Parameters

liveboolean · query
true: only alerts up right now. false: only ended ones.
curl https://app.signblink.com/api/v1/alerts \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response
{
  "alerts": [
    {
      "id": "alr_Po9Iu8Yt7Re6Wq5As4",
      "style": "emergency",
      "title": "Lockdown",
      "body": "Stay in your room. Lock the door. Await instructions.",
      "designId": null,
      "allScreens": true,
      "sound": true,
      "live": true,
      "startsAt": "2026-10-24T14:05:00.000Z",
      "endsAt": null,
      "endedAt": null,
      "source": null,
      "screens": [
        {
          "id": "scr_8Kq2mXa1bC3dE4fG5h",
          "name": "Front counter",
          "online": true,
          "showing": true
        }
      ]
    }
  ]
}

Send an alert

POST/v1/alertsFull-access key

Takes over screens straight away with a full-screen message (or a design), with no publishing. It stays up until it ends by itself (minutes) or you end it. Up to 10 alerts a minute per account.

Body

stylestringrequired
Red emergency, amber warning, blue announcement, or one of your designs. One of emergency, warning, info, design.
titlestring
The headline (required unless style is design). Up to 120 characters.
bodystring
Details under the headline. Up to 600 characters.
designIdstring
style design: the design to show.
allScreensboolean
Every screen in the account. Default false.
screenIdsarray
Otherwise, these screens.
soundboolean
Play an alert tone when it appears. Default false.
minutesinteger
End by itself after this many minutes (up to 7 days). Leave out to keep it up until ended.
curl -X POST https://app.signblink.com/api/v1/alerts \
  -H "Authorization: Bearer $SIGNBLINK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"style":"emergency","title":"Lockdown","body":"Stay in your room. Lock the door. Await instructions.","allScreens":true,"sound":true}'
Response
{
  "alert": {
    "id": "alr_Po9Iu8Yt7Re6Wq5As4",
    "style": "emergency",
    "title": "Lockdown",
    "body": "Stay in your room. Lock the door. Await instructions.",
    "designId": null,
    "allScreens": true,
    "sound": true,
    "live": true,
    "startsAt": "2026-10-24T14:05:00.000Z",
    "endsAt": null,
    "endedAt": null,
    "source": null,
    "screens": [
      {
        "id": "scr_8Kq2mXa1bC3dE4fG5h",
        "name": "Front counter",
        "online": true,
        "showing": true
      }
    ]
  }
}

End an alert

POST/v1/alerts/{id}/endFull-access key

Takes an alert down now; screens go back to what they were playing.

Parameters

idstring · in the pathrequired
The alert’s id.
curl -X POST https://app.signblink.com/api/v1/alerts/alr_Po9Iu8Yt7Re6Wq5As4/end \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response
{
  "alert": {
    "id": "alr_Po9Iu8Yt7Re6Wq5As4",
    "style": "emergency",
    "title": "Lockdown",
    "body": "Stay in your room. Lock the door. Await instructions.",
    "designId": null,
    "allScreens": true,
    "sound": true,
    "live": false,
    "startsAt": "2026-10-24T14:05:00.000Z",
    "endsAt": null,
    "endedAt": "2026-10-24T14:31:27.000Z",
    "source": null,
    "screens": [
      {
        "id": "scr_8Kq2mXa1bC3dE4fG5h",
        "name": "Front counter",
        "online": true,
        "showing": true
      }
    ]
  }
}

Webhooks

Have SignBlink send events to your address when things happen, instead of asking. See the Webhooks guide above.

List webhooks

GET/v1/webhooksAny key

The account’s webhooks (made here or in Settings → API), and every event type they can receive.

curl https://app.signblink.com/api/v1/webhooks \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response
{
  "webhooks": [
    {
      "id": "wh_Qa1Ws2Ed3Rf4Tg5Yh6U",
      "name": "Zapier",
      "url": "https://hooks.zapier.com/hooks/catch/123456/abcdef/",
      "events": [
        "screen.offline",
        "screen.online"
      ],
      "createdAt": "2026-10-24T14:05:00.000Z",
      "paused": false,
      "lastStatus": 200,
      "lastDeliveryAt": "2026-10-24T13:12:44.000Z"
    }
  ],
  "events": [
    {
      "type": "screen.offline",
      "description": "A connected screen stopped checking in for a few minutes."
    }
  ]
}

Create a webhook

POST/v1/webhooksFull-access key

Starts sending events to an https address. The secret signs every delivery; it’s shown here once (account owners can see it again in Settings → API). Integrations like Zapier create one when a Zap is turned on and delete it when it’s turned off; a delivery answered with 410 Gone also removes it.

Body

urlstringrequired
Where to POST events. Must start with https://.
eventsarray
Event types to send (see List webhooks). Leave out for all of them.
namestring
A label, shown in Settings → API.
curl -X POST https://app.signblink.com/api/v1/webhooks \
  -H "Authorization: Bearer $SIGNBLINK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://hooks.zapier.com/hooks/catch/123456/abcdef/","events":["screen.offline","screen.online"],"name":"Zapier"}'
Response
{
  "webhook": {
    "id": "wh_Qa1Ws2Ed3Rf4Tg5Yh6U",
    "name": "Zapier",
    "url": "https://hooks.zapier.com/hooks/catch/123456/abcdef/",
    "events": [
      "screen.offline",
      "screen.online"
    ],
    "createdAt": "2026-10-24T14:05:00.000Z",
    "paused": false,
    "lastStatus": 200,
    "lastDeliveryAt": "2026-10-24T13:12:44.000Z"
  },
  "secret": "whsec_9f2Kd7sLq0Vx3Rb8Nw5Ty1Hc6Pm4Ze2A"
}

Delete a webhook

DELETE/v1/webhooks/{id}Full-access key

Stops sending events to it. Deliveries already queued are dropped.

Parameters

idstring · in the pathrequired
The webhook’s id.
curl -X DELETE https://app.signblink.com/api/v1/webhooks/alr_Po9Iu8Yt7Re6Wq5As4 \
  -H "Authorization: Bearer $SIGNBLINK_KEY"
Response
{
  "ok": true
}