Developers
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.
Quick start
- Create a key. In SignBlink, go to Settings → API and choose Create a key. Only account owners can.
- Keep it secret. Store it where your code runs, for example in an environment variable called
SIGNBLINK_KEY. - Make a call. This answers with your account’s name:
curl https://app.signblink.com/api/v1/me \
-H "Authorization: Bearer $SIGNBLINK_KEY"
const res = await fetch('https://app.signblink.com/api/v1/me', {
headers: { Authorization: `Bearer ${process.env.SIGNBLINK_KEY}` },
});
console.log(await res.json());
import os, requests
res = requests.get("https://app.signblink.com/api/v1/me", headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"})
print(res.json())
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 bemultipart/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 itemfromanduntilare the screen’s local time, like2026-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"
}
}| Status | Code | What it means |
|---|---|---|
| 400 | bad_request | Something in the request isn’t right; message says what, and field which field. |
| 401 | unauthorized | No key, a malformed key, or a deleted one. |
| 402 | limit_reached | The account is over a plan limit (storage, screens), or paused for billing. |
| 403 | forbidden | A read-only key tried to change something, or the account is closed. |
| 404 | not_found | No such screen, playlist, file or alert in this account (or no such endpoint). |
| 413 | too_large | The uploaded file is over the size limit. |
| 429 | rate_limited | Too many requests. Wait a few seconds and try again. |
| 500 | server_error | Something went wrong on our side. Try again; if it keeps happening, tell us. |
| 507 | server_full | Our 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": trueto 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
unpublishedChangessays 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:
| Event | When |
|---|---|
screen.offline | A connected screen stopped checking in for a few minutes. |
screen.online | A screen that went offline is checking in normally again. |
screens.published | Someone (or an API key) published changes to screens. |
alert.sent | An emergency alert or announcement went up, including automatic weather warnings. |
alert.ended | An alert came down, by hand or at its end time. |
{
"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 || ''));
}
import hashlib, hmac, time
def from_signblink(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
if abs(time.time() - int(parts["t"])) > 300:
return False
expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("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
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"
const res = await fetch('https://app.signblink.com/api/v1/me', {
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const data = await res.json();
import os, requests
res = requests.get(
"https://app.signblink.com/api/v1/me",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
data = res.json()
{
"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"
const res = await fetch('https://app.signblink.com/api/v1/screens', {
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const data = await res.json();
import os, requests
res = requests.get(
"https://app.signblink.com/api/v1/screens",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
data = res.json()
{
"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"
const res = await fetch('https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h', {
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const data = await res.json();
import os, requests
res = requests.get(
"https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
data = res.json()
{
"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}'
const res = await fetch('https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"playlistId": "pl_Lm4nOp5qRs6tUv7wXy",
"publish": true
}),
});
const data = await res.json();
import os, requests
res = requests.patch(
"https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
json={"playlistId":"pl_Lm4nOp5qRs6tUv7wXy","publish":True},
)
data = res.json()
{
"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"
const res = await fetch('https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h/restart', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const data = await res.json();
import os, requests
res = requests.post(
"https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h/restart",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
data = res.json()
{
"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"
const res = await fetch('https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h/screenshot', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const data = await res.json();
import os, requests
res = requests.post(
"https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h/screenshot",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
data = res.json()
{
"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"
const res = await fetch('https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h/screenshot', {
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const image = await res.arrayBuffer();
import os, requests
res = requests.get(
"https://app.signblink.com/api/v1/screens/scr_8Kq2mXa1bC3dE4fG5h/screenshot",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
open("screenshot.jpg", "wb").write(res.content)
(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"
const res = await fetch('https://app.signblink.com/api/v1/groups', {
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const data = await res.json();
import os, requests
res = requests.get(
"https://app.signblink.com/api/v1/groups",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
data = res.json()
{
"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"
const res = await fetch('https://app.signblink.com/api/v1/playlists', {
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const data = await res.json();
import os, requests
res = requests.get(
"https://app.signblink.com/api/v1/playlists",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
data = res.json()
{
"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"
const res = await fetch('https://app.signblink.com/api/v1/playlists/pl_Lm4nOp5qRs6tUv7wXy', {
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const data = await res.json();
import os, requests
res = requests.get(
"https://app.signblink.com/api/v1/playlists/pl_Lm4nOp5qRs6tUv7wXy",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
data = res.json()
{
"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}]}'
const res = await fetch('https://app.signblink.com/api/v1/playlists', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"name": "Lunch specials",
"items": [
{
"type": "media",
"refId": "med_Zx9Cv8Bn7Mm6Ll5Kk4",
"durationSec": 8
}
]
}),
});
const data = await res.json();
import os, requests
res = requests.post(
"https://app.signblink.com/api/v1/playlists",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
json={"name":"Lunch specials","items":[{"type":"media","refId":"med_Zx9Cv8Bn7Mm6Ll5Kk4","durationSec":8}]},
)
data = res.json()
{
"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}'
const res = await fetch('https://app.signblink.com/api/v1/playlists/pl_Lm4nOp5qRs6tUv7wXy', {
method: 'PUT',
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"items": [
{
"type": "media",
"refId": "med_Zx9Cv8Bn7Mm6Ll5Kk4",
"durationSec": 10
},
{
"type": "design",
"refId": "dsg_Hg5Fd4Sa3Qw2Er1Ty0",
"durationSec": 15
}
],
"publish": true
}),
});
const data = await res.json();
import os, requests
res = requests.put(
"https://app.signblink.com/api/v1/playlists/pl_Lm4nOp5qRs6tUv7wXy",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
json={"items":[{"type":"media","refId":"med_Zx9Cv8Bn7Mm6Ll5Kk4","durationSec":10},{"type":"design","refId":"dsg_Hg5Fd4Sa3Qw2Er1Ty0","durationSec":15}],"publish":True},
)
data = res.json()
{
"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}'
const res = await fetch('https://app.signblink.com/api/v1/playlists/pl_Lm4nOp5qRs6tUv7wXy/items', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"type": "media",
"refId": "med_Zx9Cv8Bn7Mm6Ll5Kk4",
"durationSec": 10,
"publish": true
}),
});
const data = await res.json();
import os, requests
res = requests.post(
"https://app.signblink.com/api/v1/playlists/pl_Lm4nOp5qRs6tUv7wXy/items",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
json={"type":"media","refId":"med_Zx9Cv8Bn7Mm6Ll5Kk4","durationSec":10,"publish":True},
)
data = res.json()
{
"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"
const res = await fetch('https://app.signblink.com/api/v1/playlists/pl_Lm4nOp5qRs6tUv7wXy/items/it_Q1w2E3r4T5', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const data = await res.json();
import os, requests
res = requests.delete(
"https://app.signblink.com/api/v1/playlists/pl_Lm4nOp5qRs6tUv7wXy/items/it_Q1w2E3r4T5",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
data = res.json()
{
"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"
const res = await fetch('https://app.signblink.com/api/v1/media', {
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const data = await res.json();
import os, requests
res = requests.get(
"https://app.signblink.com/api/v1/media",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
data = res.json()
{
"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"
const res = await fetch('https://app.signblink.com/api/v1/media/med_Zx9Cv8Bn7Mm6Ll5Kk4', {
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const data = await res.json();
import os, requests
res = requests.get(
"https://app.signblink.com/api/v1/media/med_Zx9Cv8Bn7Mm6Ll5Kk4",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
data = res.json()
{
"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"}'
const res = await fetch('https://app.signblink.com/api/v1/media', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"url": "https://example.com/menu/pumpkin-latte.jpg",
"name": "Pumpkin latte"
}),
});
const data = await res.json();
import os, requests
res = requests.post(
"https://app.signblink.com/api/v1/media",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
json={"url":"https://example.com/menu/pumpkin-latte.jpg","name":"Pumpkin latte"},
)
data = res.json()
{
"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"
const res = await fetch('https://app.signblink.com/api/v1/media/med_Zx9Cv8Bn7Mm6Ll5Kk4', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const data = await res.json();
import os, requests
res = requests.delete(
"https://app.signblink.com/api/v1/media/med_Zx9Cv8Bn7Mm6Ll5Kk4",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
data = res.json()
{
"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"
const res = await fetch('https://app.signblink.com/api/v1/designs', {
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const data = await res.json();
import os, requests
res = requests.get(
"https://app.signblink.com/api/v1/designs",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
data = res.json()
{
"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"}'
const res = await fetch('https://app.signblink.com/api/v1/publish', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"groupId": "grp_Ab12Cd34Ef56Gh78Ij"
}),
});
const data = await res.json();
import os, requests
res = requests.post(
"https://app.signblink.com/api/v1/publish",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
json={"groupId":"grp_Ab12Cd34Ef56Gh78Ij"},
)
data = res.json()
{
"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"
const res = await fetch('https://app.signblink.com/api/v1/alerts', {
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const data = await res.json();
import os, requests
res = requests.get(
"https://app.signblink.com/api/v1/alerts",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
data = res.json()
{
"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}'
const res = await fetch('https://app.signblink.com/api/v1/alerts', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"style": "emergency",
"title": "Lockdown",
"body": "Stay in your room. Lock the door. Await instructions.",
"allScreens": true,
"sound": true
}),
});
const data = await res.json();
import os, requests
res = requests.post(
"https://app.signblink.com/api/v1/alerts",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
json={"style":"emergency","title":"Lockdown","body":"Stay in your room. Lock the door. Await instructions.","allScreens":True,"sound":True},
)
data = res.json()
{
"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"
const res = await fetch('https://app.signblink.com/api/v1/alerts/alr_Po9Iu8Yt7Re6Wq5As4/end', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const data = await res.json();
import os, requests
res = requests.post(
"https://app.signblink.com/api/v1/alerts/alr_Po9Iu8Yt7Re6Wq5As4/end",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
data = res.json()
{
"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"
const res = await fetch('https://app.signblink.com/api/v1/webhooks', {
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const data = await res.json();
import os, requests
res = requests.get(
"https://app.signblink.com/api/v1/webhooks",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
data = res.json()
{
"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"}'
const res = await fetch('https://app.signblink.com/api/v1/webhooks', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"url": "https://hooks.zapier.com/hooks/catch/123456/abcdef/",
"events": [
"screen.offline",
"screen.online"
],
"name": "Zapier"
}),
});
const data = await res.json();
import os, requests
res = requests.post(
"https://app.signblink.com/api/v1/webhooks",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
json={"url":"https://hooks.zapier.com/hooks/catch/123456/abcdef/","events":["screen.offline","screen.online"],"name":"Zapier"},
)
data = res.json()
{
"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"
const res = await fetch('https://app.signblink.com/api/v1/webhooks/alr_Po9Iu8Yt7Re6Wq5As4', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.SIGNBLINK_KEY}`,
},
});
const data = await res.json();
import os, requests
res = requests.delete(
"https://app.signblink.com/api/v1/webhooks/alr_Po9Iu8Yt7Re6Wq5As4",
headers={"Authorization": f"Bearer {os.environ['SIGNBLINK_KEY']}"},
)
data = res.json()
{
"ok": true
}