Trip Light
Contents

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

  1. Open Account settings and find REST API.
  2. Give the new token a name - what it is for, like Home Assistant - and choose Create token.
  3. 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:

StatusMeaning
200Done
400Something in the request cannot be used - the error says what
401No token, or one that does not exist (any more)
403The account is locked
404No such light, group, preset ... in your account
405That address takes another method - the Allow header says which
409Not possible right now, for example the light is offline
429Over 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:

HeaderMeaning
X-RateLimit-LimitRequests allowed in the window
X-RateLimit-RemainingHow many are left right now
X-RateLimit-ResetSeconds until the oldest request leaves the window
X-RateLimit-WindowThe 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

RequestWhat it does
GET /lightsEvery 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}/stateChanges what it shows - see below
POST /lights/{id}/identifyFlashes 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}/runtimeLast until a time: {"minutes": 240}, {"until": 1790000000} (UTC seconds) or {"clear": true}
GET /lights/{id}/effectsIts effects: id, name and what its sliders are
GET /lights/{id}/palettesIts palettes, and its own (Custom 0, 1 ...) with their colours
GET /lights/{id}/presetsThe presets and playlists on the light
POST /lights/{id}/presetsChanges 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}/saveSaves one of its presets to My Presets
POST /lights/{id}/palettes/{slot}/saveSaves one of its own palettes to My Palettes
PUT /lights/{id}/partsWhat each part follows - {"0": "group", "1": "own"} or a shared group's id
PATCH /lights/{id}{"name": "..."} renames it, {"group": 3} moves it
POST /lightsAdds 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:

FieldValue
ontrue or false
toggletrue switches it the other way
briBrightness, 0-255 - 0 switches it off
presetPlays a preset or playlist on the light, 1-250
fx, palEffect and palette, by id
sx, ix, c1, c2The effect's sliders, 0-255
c3The effect's third custom slider, 0-31
o1, o2, o3The effect's options, true or false
siThe sound simulation, 0-3
colUp to three colours: "#rrggbb" or [r, g, b], null to keep one
partsFor a light with parts: the ids of the parts the change is for - all of them without it
part_on, part_briA 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

RequestWhat it does
GET /groupsEvery 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, DELETEOne group; {"name": "..."} renames it; deleting moves its lights to the default group
POST /groups/{id}/stateWhat 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}/settingscloud_sync, local_sync, audio, link (Ableton Link) and random_seed_sync, each true or false
POST /groups/{id}/runtimeA runtime target for every light of the group, as for a light
GET, PUT /groups/{id}/randpalRandom 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}/shareShares 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

RequestWhat it does
GET /presetsMy Presets: id, name, kind of light, whether it is a playlist
GET /presets/{id}One, with all its settings
POST /presetsA 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}/copyCopies 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}/tryPlays 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 /playlistsA 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 /liveShows 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

RequestWhat it does
GET /palettesMy 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}/tryShows it for 30 seconds: {"target": "l12"}

Community

RequestWhat it does
GET /community/presets?q=fireShared presets, searched by name, description and username
GET /community/presets/{id}One, with its settings
POST /community/presets/{id}/saveSaves it to My Presets
POST /community/presets/{id}/tryPlays it for 30 seconds: {"target": "l12"}
GET .../{id}/plan, POST .../{id}/copyCopies it straight to a light, as for your own
GET /community/palettesShared 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

RequestWhat it does
GET /sharesYour 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

RequestWhat it does
GET /accountYour username, the token's name, and the rate limit with what is left
GET, PATCH /account/prefsThe panel's settings: sort (added, name, battery), online, bgfx, light, pvgroup, pvlight, pvpanel, pvlive