REST API
The REST API lets scripts and home automation do what the cloud panel does: switch lights and groups, set effects, palettes and colours, play presets, and manage presets, palettes, groups and sharing - from anywhere, as JSON over HTTPS.
Every request is made with a token from your account, and a token reaches your account and nothing else: your lights, groups, presets and palettes - never anybody else's.
Tokens
- Open Account settings and find REST API.
- Give the new token a name - what it is for, like Home Assistant - and choose Create token.
- Copy the token. It starts with
tl_and is shown once: the panel keeps only a fingerprint of it and cannot show it again.
The list under REST API shows each token's name, its first characters, when it was made and last used, and how many requests it has made. Rename a token there, or delete it - whatever uses it stops working at once. An account has up to 20 tokens.
Keep a token as secret as a password: anybody who has it can control your lights. If one gets out, delete it and make a new one.
Making a request
Every request goes to https://triplight.app/api/v1/... with the token in its Authorization header. A request with a body sends JSON.
curl https://triplight.app/api/v1/lights \
-H "Authorization: Bearer tl_your_token"
curl -X POST https://triplight.app/api/v1/lights/12/state \
-H "Authorization: Bearer tl_your_token" \
-H "Content-Type: application/json" \
-d '{"on": true, "bri": 140, "fx": 9, "col": ["#ff5000", null, "#0050ff"]}'Every answer is JSON with "ok": true, or false with an "error" that says what is wrong, in words. The status code says the same:
| Status | Meaning |
|---|---|
| 200 | Done |
| 400 | Something in the request cannot be used - the error says what |
| 401 | No token, or one that does not exist (any more) |
| 403 | The account is locked |
| 404 | No such light, group, preset ... in your account |
| 405 | That address takes another method - the Allow header says which |
| 409 | Not possible right now, for example the light is offline |
| 429 | Over the rate limit - see below |
GET /api/v1 lists every address the API has.
The rate limit
An account may make 300 requests in any 5 minutes - one a second, on average. The window slides: 300 requests in one minute are fine, and then the next goes through once the oldest of them is five minutes old. The limit is the account's, shared by all of its tokens, so more tokens do not make more requests.
Every answer says where you are:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed in the window |
X-RateLimit-Remaining | How many are left right now |
X-RateLimit-Reset | Seconds until the oldest request leaves the window |
X-RateLimit-Window | The window, in seconds |
Over the limit, the answer is 429 with a Retry-After header: the seconds until the next request goes through. Account settings shows your requests over the window as a graph, live, and how much of the limit is left.
Lights
| Request | What it does |
|---|---|
GET /lights | Every light of the account: its id, name, group, kind, whether it is online and on, brightness, effect, palette, sliders, colours, battery and more - and link, what its Ableton Link is doing: on, peers, sync (on the beat), bpm, play (null on firmware without Link) |
GET /lights/{id} | One light, and the runtime of its group's lights |
POST /lights/{id}/state | Changes what it shows - see below |
POST /lights/{id}/identify | Flashes it twice, to tell it apart |
POST /lights/{id}/settings | {"local": true} local sync, {"audio": true} audio reactive, {"audio_mode": 0} network audio - 0 off, 1 send, 2 receive, {"link": true} Ableton Link - which switches audio reactive off, as audio reactive switches it off |
POST /lights/{id}/runtime | Last until a time: {"minutes": 240}, {"until": 1790000000} (UTC seconds) or {"clear": true} |
GET /lights/{id}/effects | Its effects: id, name and what its sliders are |
GET /lights/{id}/palettes | Its palettes, and its own (Custom 0, 1 ...) with their colours |
GET /lights/{id}/presets | The presets and playlists on the light |
POST /lights/{id}/presets | Changes them, as the Presets tab does: {"action": "current", "name": "Evening"} saves what it shows, "edit" renames or renumbers one (pid, to, name), "boot" sets the one it starts with (pid), "playlist" makes or changes a playlist |
POST /lights/{id}/presets/{number}/save | Saves one of its presets to My Presets |
POST /lights/{id}/palettes/{slot}/save | Saves one of its own palettes to My Palettes |
PUT /lights/{id}/parts | What each part follows - {"0": "group", "1": "own"} or a shared group's id |
PATCH /lights/{id} | {"name": "..."} renames it, {"group": 3} moves it |
POST /lights | Adds a light with its connection token: {"token": "...", "group": 3} |
DELETE /lights/{id} | Takes it off the account - it keeps working and keeps its settings |
What a light shows
POST /lights/{id}/state takes any of these, and changes only what it is given:
| Field | Value |
|---|---|
on | true or false |
toggle | true switches it the other way |
bri | Brightness, 0-255 - 0 switches it off |
preset | Plays a preset or playlist on the light, 1-250 |
fx, pal | Effect and palette, by id |
sx, ix, c1, c2 | The effect's sliders, 0-255 |
c3 | The effect's third custom slider, 0-31 |
o1, o2, o3 | The effect's options, true or false |
si | The sound simulation, 0-3 |
col | Up to three colours: "#rrggbb" or [r, g, b], null to keep one |
parts | For a light with parts: the ids of the parts the change is for - all of them without it |
part_on, part_bri | A part's own power and brightness, with parts |
A light of a group that syncs through the cloud passes the change on to every light of the group, as it does in the panel.
Groups
| Request | What it does |
|---|---|
GET /groups | Every group: its lights, whether it syncs through the cloud, keeps its presets synced, is shared or follows a share |
POST /groups | {"name": "Kitchen"} makes one |
GET /groups/{id}, PATCH, DELETE | One group; {"name": "..."} renames it; deleting moves its lights to the default group |
POST /groups/{id}/state | What the whole group shows - the same fields as a light. A group that syncs through the cloud takes it at once, any other light by light |
POST /groups/{id}/settings | cloud_sync, local_sync, audio, link (Ableton Link) and random_seed_sync, each true or false |
POST /groups/{id}/runtime | A runtime target for every light of the group, as for a light |
GET, PUT /groups/{id}/randpal | Random Cycle for the group: on, style (pastel, normal, vivid), scheme (any, analogous, triadic, split, square, tetradic, random), count (1-6), every (seconds), smooth |
GET /groups/{id}/presets/plan?seed={light id} | What keeping the presets synced from that light would change |
PUT /groups/{id}/presets | {"on": true, "seed": 12} keeps the presets synced, from light 12; {"on": false} stops |
PUT /groups/{id}/share | Shares the group: {"visibility": "private"} (your groups only), "named", "public", or "off" |
PUT /groups/{id}/follow, DELETE | {"share": 4} follows a shared group; DELETE stops following |
Presets and playlists
| Request | What it does |
|---|---|
GET /presets | My Presets: id, name, kind of light, whether it is a playlist |
GET /presets/{id} | One, with all its settings |
POST /presets | A new one from a look: {"name": "Ember", "look": {...}}, and "kind" (ice, cube, crystal, stripe, panel) |
PATCH /presets/{id} | {"name": "..."} renames it, {"look": {...}} changes its look |
DELETE /presets/{id} | Deletes it from the cloud - lights keep their copy |
GET /presets/{id}/plan?light={id} | What copying it there would do (or group=) |
POST /presets/{id}/copy | Copies it to {"light": 12} or {"group": 3} - at the numbers the copy dialog would pick, or at your own with "map": {"{its number}": {number on the light}} |
POST /presets/{id}/try | Plays it for 30 seconds: {"target": "l12"} (light 12) or "g3" (group 3) - a group that syncs through the cloud is tried as a whole, not light by light, as in the panel |
POST /try/stop | {"target": "l12"} ends a try early |
POST /playlists | A playlist from saved presets: name; entries, each a saved preset and its seconds - [{"saved": 5, "dur": 10}, ...]; transition (seconds); repeat (0 plays it forever); shuffle; end, the saved preset to play when it is over |
POST /live | Shows a look on a light while you make it: {"target": "l12", "look": {...}} - send it again within a minute, or the light goes back |
A look is what the effect designer makes: fx, pal, sx, ix, c1, c2, c3, si, o1, o2, o3 and col - all of them, col with three colours as [r, g, b]:
{"fx": 2, "pal": 6, "sx": 128, "ix": 128, "c1": 128, "c2": 128, "c3": 16, "si": 0,
"o1": false, "o2": false, "o3": false,
"col": [[255, 80, 0], [0, 0, 0], [0, 0, 0]]}Palettes
| Request | What it does |
|---|---|
GET /palettes | My Palettes, with their colours |
POST /palettes | {"name": "Sunset", "stops": [[0, 255, 60, 0], [255, 120, 0, 200]]} - each stop a position (0-255) and a colour, 2 to 18 of them |
GET, PATCH, DELETE /palettes/{id} | One; {"name"} or {"stops"} changes it |
GET /palettes/{id}/plan?light={id} | The slots on the light, and which are in use |
POST /palettes/{id}/copy | {"light": 12, "slot": 0} puts it in Custom 0 there (or "group") |
POST /palettes/{id}/try | Shows it for 30 seconds: {"target": "l12"} |
Community
| Request | What it does |
|---|---|
GET /community/presets?q=fire | Shared presets, searched by name, description and username |
GET /community/presets/{id} | One, with its settings |
POST /community/presets/{id}/save | Saves it to My Presets |
POST /community/presets/{id}/try | Plays it for 30 seconds: {"target": "l12"} |
GET .../{id}/plan, POST .../{id}/copy | Copies it straight to a light, as for your own |
GET /community/palettes | Shared palettes - with save and try as for presets |
Sharing on Community, changing or taking back what you shared, and stars are for the panel: the API reads Community and tries, saves and copies from it.
Shared Sync
| Request | What it does |
|---|---|
GET /shares | Your shared groups, those shared with you and the public ones, and who follows what |
POST /shares/{id}/accounts | {"username": "..."} lets an account follow a share of yours |
DELETE /shares/{id}/accounts/{user id} | Takes it off again |
PUT /shares/{id}/accounts/{user id}/writer | {"writer": true} lets them change the look |
Account
| Request | What it does |
|---|---|
GET /account | Your username, the token's name, and the rate limit with what is left |
GET, PATCH /account/prefs | The panel's settings: sort (added, name, battery), online, bgfx, light, pvgroup, pvlight, pvpanel, pvlive |