{"openapi":"3.1.0","info":{"title":"CueStride public API","version":"1.0.0","description":"Read and run your CueStride rooms with an API key an account's admin makes in Account settings. Additive within v1: new routes, optional fields and codes, never a field removed or changed in meaning. A string's length counts UTF-16 code units, as JavaScript's does, so a character outside the Basic Multilingual Plane, such as most emoji, counts as two. A fault in the server answers 500 INTERNAL in the same error envelope, on any route."},"servers":[{"url":"https://app.cuestride.com/api/v1"}],"security":[{"bearerKey":[]}],"tags":[{"name":"Reads"},{"name":"Stream"},{"name":"Control"},{"name":"Edit"},{"name":"Reference"}],"paths":{"/openapi.json":{"get":{"operationId":"getOpenApi","summary":"This description. It needs no key, and a key left in its query is refused, as on every route but a button link and the stream.","tags":["Reference"],"security":[],"responses":{"200":{"description":"The OpenAPI 3.1 description.","content":{"application/json":{"schema":{}}}},"400":{"$ref":"#/components/responses/BadRequest"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms":{"get":{"operationId":"listRooms","summary":"The rooms in the key's reach.","tags":["Reads"],"x-scope":"read","responses":{"200":{"description":"The rooms.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"rooms":{"type":"array","items":{"$ref":"#/components/schemas/RoomListItem"}}},"required":["ok","rooms"],"additionalProperties":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}":{"get":{"operationId":"getRoom","summary":"The room's settings.","tags":["Reads"],"x-scope":"read","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"Read.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}},"ETag":{"description":"The room's revision.","required":true,"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"revision":{"type":"integer","minimum":0},"room":{"$ref":"#/components/schemas/RoomSettings"}},"required":["ok","revision","room"],"additionalProperties":false}}}},"304":{"description":"Unchanged since the revision the caller holds.","headers":{"ETag":{"description":"The room's revision.","required":true,"schema":{"type":"string"}},"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateRoom","summary":"Change the room's settings.","tags":["Edit"],"x-scope":"edit","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomUpdate"}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/status":{"get":{"operationId":"getStatus","summary":"The room's status, for pollers.","tags":["Reads"],"x-scope":"read","parameters":[{"$ref":"#/components/parameters/RoomId"}],"responses":{"200":{"description":"The status.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/timers":{"get":{"operationId":"getTimers","summary":"The rundown, notes included. Like a write that sends the rundown, it counts as one read per 100 timers the room holds, at least one; a 304 counts as one.","tags":["Reads"],"x-scope":"read","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"Read.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}},"ETag":{"description":"The room's revision.","required":true,"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"revision":{"type":"integer","minimum":0},"timers":{"type":"array","items":{"$ref":"#/components/schemas/Timer"}}},"required":["ok","revision","timers"],"additionalProperties":false}}}},"304":{"description":"Unchanged since the revision the caller holds.","headers":{"ETag":{"description":"The room's revision.","required":true,"schema":{"type":"string"}},"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createTimer","summary":"Add a timer. A write that changes the rundown or the messages counts as one write per 100 items the room holds, at least one.","tags":["Edit"],"x-scope":"edit","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimerBody"}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimersAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/messages":{"get":{"operationId":"getMessages","summary":"The messages. It counts as one read per 100 messages the room holds, at least one; a 304 counts as one.","tags":["Reads"],"x-scope":"read","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/IfNoneMatch"}],"responses":{"200":{"description":"Read.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}},"ETag":{"description":"The room's revision.","required":true,"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"const":true},"revision":{"type":"integer","minimum":0},"messages":{"type":"array","items":{"$ref":"#/components/schemas/Message"}}},"required":["ok","revision","messages"],"additionalProperties":false}}}},"304":{"description":"Unchanged since the revision the caller holds.","headers":{"ETag":{"description":"The room's revision.","required":true,"schema":{"type":"string"}},"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createMessage","summary":"Add a message. A write that changes the rundown or the messages counts as one write per 100 items the room holds, at least one.","tags":["Edit"],"x-scope":"edit","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageBody"}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessagesAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/events":{"get":{"operationId":"openEvents","summary":"The room's moderator stream, attributed to the key: every message, timer notes blank. One per key per room; a new one replaces the key's old one there, which gets kicked with the reason replaced; 10 per key across rooms. A HEAD answers 405.","tags":["Stream"],"x-scope":"read","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"name":"lists","in":"query","required":false,"description":"items asks for the lists as item changes instead of whole, recovered by reopening the stream on a gap, a change that does not fit, or a heartbeat digest that differs. Without it, or with any other value, the lists come whole, and no value is refused.","schema":{"type":"string","examples":["items"]}}],"responses":{"200":{"description":"Server-sent events: snapshot, update, heartbeat, and the per-connection events.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"text/event-stream":{"schema":{"type":"string"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/playback/start":{"post":{"operationId":"startPlayback","summary":"Start the loaded timer.","tags":["Control"],"x-scope":"control","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"type":"object","properties":{"ifRevision":{"type":"integer","minimum":0}},"additionalProperties":false}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"startPlaybackByLink","summary":"Start the loaded timer. A button link: the parameters in the query, for a key made for links. A HEAD answers 405, so a link checker never presses it.","tags":["Control"],"x-scope":"control","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"ifRevision","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/playback/stop":{"post":{"operationId":"stopPlayback","summary":"Stop the loaded timer.","tags":["Control"],"x-scope":"control","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"type":"object","properties":{"ifRevision":{"type":"integer","minimum":0}},"additionalProperties":false}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"stopPlaybackByLink","summary":"Stop the loaded timer. A button link: the parameters in the query, for a key made for links. A HEAD answers 405, so a link checker never presses it.","tags":["Control"],"x-scope":"control","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"ifRevision","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/playback/toggle":{"post":{"operationId":"togglePlayback","summary":"Toggle the loaded timer.","tags":["Control"],"x-scope":"control","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"type":"object","properties":{"ifRevision":{"type":"integer","minimum":0}},"additionalProperties":false}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"togglePlaybackByLink","summary":"Toggle the loaded timer. A button link: the parameters in the query, for a key made for links. A HEAD answers 405, so a link checker never presses it.","tags":["Control"],"x-scope":"control","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"ifRevision","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/playback/next":{"post":{"operationId":"nextPlayback","summary":"Cue the next timer, and start it with autostart.","tags":["Control"],"x-scope":"control","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"type":"object","properties":{"autostart":{"type":"boolean"},"ifRevision":{"type":"integer","minimum":0}},"additionalProperties":false}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"nextPlaybackByLink","summary":"Cue the next timer, and start it with autostart. A button link: the parameters in the query, for a key made for links. A HEAD answers 405, so a link checker never presses it.","tags":["Control"],"x-scope":"control","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"autostart","in":"query","required":false,"schema":{"type":"boolean"}},{"name":"ifRevision","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/playback/previous":{"post":{"operationId":"previousPlayback","summary":"Cue the previous timer, and start it with autostart.","tags":["Control"],"x-scope":"control","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"type":"object","properties":{"autostart":{"type":"boolean"},"ifRevision":{"type":"integer","minimum":0}},"additionalProperties":false}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"previousPlaybackByLink","summary":"Cue the previous timer, and start it with autostart. A button link: the parameters in the query, for a key made for links. A HEAD answers 405, so a link checker never presses it.","tags":["Control"],"x-scope":"control","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"autostart","in":"query","required":false,"schema":{"type":"boolean"}},{"name":"ifRevision","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/playback/reset":{"post":{"operationId":"resetPlayback","summary":"Reset the loaded timer, and start it with autostart.","tags":["Control"],"x-scope":"control","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"type":"object","properties":{"autostart":{"type":"boolean"},"ifRevision":{"type":"integer","minimum":0}},"additionalProperties":false}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"resetPlaybackByLink","summary":"Reset the loaded timer, and start it with autostart. A button link: the parameters in the query, for a key made for links. A HEAD answers 405, so a link checker never presses it.","tags":["Control"],"x-scope":"control","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"autostart","in":"query","required":false,"schema":{"type":"boolean"}},{"name":"ifRevision","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/playback/select":{"post":{"operationId":"selectTimer","summary":"Cue a timer by its id or its printed number, and start it with autostart. Give exactly one of timerId or index.","tags":["Control"],"x-scope":"control","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"type":"object","properties":{"timerId":{"type":"string","minLength":1,"maxLength":64},"index":{"type":"integer","minimum":1,"maximum":10000,"description":"The number the controller prints."},"autostart":{"type":"boolean"},"ifRevision":{"type":"integer","minimum":0}},"additionalProperties":false,"oneOf":[{"required":["timerId"]},{"required":["index"]}]}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"selectTimerByLink","summary":"Cue a timer by its id or its printed number, and start it with autostart. Give exactly one of timerId or index. A button link: the parameters in the query, for a key made for links. A HEAD answers 405, so a link checker never presses it.","tags":["Control"],"x-scope":"control","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"timerId","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":64}},{"name":"index","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":10000,"description":"The number the controller prints."}},{"name":"autostart","in":"query","required":false,"schema":{"type":"boolean"}},{"name":"ifRevision","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/playback/add":{"post":{"operationId":"addTime","summary":"Add time to the loaded timer. Give exactly one of ms or amount.","tags":["Control"],"x-scope":"control","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"type":"object","properties":{"ms":{"type":"integer","minimum":1,"maximum":86400000},"amount":{"oneOf":[{"type":"integer","minimum":1,"maximum":86400,"description":"Seconds."},{"type":"string","maxLength":32,"pattern":"^\\s*(?:\\d+(?:\\.\\d+)?\\s*[hHmMsS]|(?=\\d)(?:\\d+\\s*[hH]\\s*)?(?:\\d+\\s*[mM]\\s*)?(?:\\d+\\s*[sS])?|\\d+|\\d{1,2}:[0-5]?\\d(?::[0-5]?\\d)?)\\s*$"}],"description":"A duration: a whole number of seconds, or text such as 30s, 5m, 1.5h, 1m30s, 5:30, or 1:05:30, with no sign. Within 24 hours, and above zero, which text can break and the server refuses. In a query, send a + as %2B."},"ifRevision":{"type":"integer","minimum":0}},"additionalProperties":false,"oneOf":[{"required":["ms"]},{"required":["amount"]}]}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"addTimeByLink","summary":"Add time to the loaded timer. Give exactly one of ms or amount. A button link: the parameters in the query, for a key made for links. A HEAD answers 405, so a link checker never presses it.","tags":["Control"],"x-scope":"control","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"ms","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":86400000}},{"name":"amount","in":"query","required":false,"schema":{"oneOf":[{"type":"integer","minimum":1,"maximum":86400,"description":"Seconds."},{"type":"string","maxLength":32,"pattern":"^\\s*(?:\\d+(?:\\.\\d+)?\\s*[hHmMsS]|(?=\\d)(?:\\d+\\s*[hH]\\s*)?(?:\\d+\\s*[mM]\\s*)?(?:\\d+\\s*[sS])?|\\d+|\\d{1,2}:[0-5]?\\d(?::[0-5]?\\d)?)\\s*$"}],"description":"A duration: a whole number of seconds, or text such as 30s, 5m, 1.5h, 1m30s, 5:30, or 1:05:30, with no sign. Within 24 hours, and above zero, which text can break and the server refuses. In a query, send a + as %2B."}},{"name":"ifRevision","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/playback/subtract":{"post":{"operationId":"subtractTime","summary":"Take time from the loaded timer. Give exactly one of ms or amount.","tags":["Control"],"x-scope":"control","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"type":"object","properties":{"ms":{"type":"integer","minimum":1,"maximum":86400000},"amount":{"oneOf":[{"type":"integer","minimum":1,"maximum":86400,"description":"Seconds."},{"type":"string","maxLength":32,"pattern":"^\\s*(?:\\d+(?:\\.\\d+)?\\s*[hHmMsS]|(?=\\d)(?:\\d+\\s*[hH]\\s*)?(?:\\d+\\s*[mM]\\s*)?(?:\\d+\\s*[sS])?|\\d+|\\d{1,2}:[0-5]?\\d(?::[0-5]?\\d)?)\\s*$"}],"description":"A duration: a whole number of seconds, or text such as 30s, 5m, 1.5h, 1m30s, 5:30, or 1:05:30, with no sign. Within 24 hours, and above zero, which text can break and the server refuses. In a query, send a + as %2B."},"ifRevision":{"type":"integer","minimum":0}},"additionalProperties":false,"oneOf":[{"required":["ms"]},{"required":["amount"]}]}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"subtractTimeByLink","summary":"Take time from the loaded timer. Give exactly one of ms or amount. A button link: the parameters in the query, for a key made for links. A HEAD answers 405, so a link checker never presses it.","tags":["Control"],"x-scope":"control","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"ms","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":86400000}},{"name":"amount","in":"query","required":false,"schema":{"oneOf":[{"type":"integer","minimum":1,"maximum":86400,"description":"Seconds."},{"type":"string","maxLength":32,"pattern":"^\\s*(?:\\d+(?:\\.\\d+)?\\s*[hHmMsS]|(?=\\d)(?:\\d+\\s*[hH]\\s*)?(?:\\d+\\s*[mM]\\s*)?(?:\\d+\\s*[sS])?|\\d+|\\d{1,2}:[0-5]?\\d(?::[0-5]?\\d)?)\\s*$"}],"description":"A duration: a whole number of seconds, or text such as 30s, 5m, 1.5h, 1m30s, 5:30, or 1:05:30, with no sign. Within 24 hours, and above zero, which text can break and the server refuses. In a query, send a + as %2B."}},{"name":"ifRevision","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/playback/jump":{"post":{"operationId":"jumpPlayhead","summary":"Move the playhead forward or back. Give exactly one of ms or amount.","tags":["Control"],"x-scope":"control","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"type":"object","properties":{"ms":{"type":"integer","minimum":-86400000,"maximum":86400000,"not":{"const":0}},"amount":{"oneOf":[{"type":"integer","minimum":-86400,"maximum":86400,"not":{"const":0},"description":"Seconds."},{"type":"string","maxLength":32,"pattern":"^\\s*(?:[+-]\\s*)?(?:\\d+(?:\\.\\d+)?\\s*[hHmMsS]|(?=\\d)(?:\\d+\\s*[hH]\\s*)?(?:\\d+\\s*[mM]\\s*)?(?:\\d+\\s*[sS])?|\\d+|\\d{1,2}:[0-5]?\\d(?::[0-5]?\\d)?)\\s*$"}],"description":"A duration: a whole number of seconds, or text such as 30s, 5m, 1.5h, 1m30s, 5:30, or 1:05:30, signed to move back: -10s. Within 24 hours, and not zero, which text can break and the server refuses. In a query, send a + as %2B."},"ifRevision":{"type":"integer","minimum":0}},"additionalProperties":false,"oneOf":[{"required":["ms"]},{"required":["amount"]}]}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"jumpPlayheadByLink","summary":"Move the playhead forward or back. Give exactly one of ms or amount. A button link: the parameters in the query, for a key made for links. A HEAD answers 405, so a link checker never presses it.","tags":["Control"],"x-scope":"control","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"ms","in":"query","required":false,"schema":{"type":"integer","minimum":-86400000,"maximum":86400000,"not":{"const":0}}},{"name":"amount","in":"query","required":false,"schema":{"oneOf":[{"type":"integer","minimum":-86400,"maximum":86400,"not":{"const":0},"description":"Seconds."},{"type":"string","maxLength":32,"pattern":"^\\s*(?:[+-]\\s*)?(?:\\d+(?:\\.\\d+)?\\s*[hHmMsS]|(?=\\d)(?:\\d+\\s*[hH]\\s*)?(?:\\d+\\s*[mM]\\s*)?(?:\\d+\\s*[sS])?|\\d+|\\d{1,2}:[0-5]?\\d(?::[0-5]?\\d)?)\\s*$"}],"description":"A duration: a whole number of seconds, or text such as 30s, 5m, 1.5h, 1m30s, 5:30, or 1:05:30, signed to move back: -10s. Within 24 hours, and not zero, which text can break and the server refuses. In a query, send a + as %2B."}},{"name":"ifRevision","in":"query","required":false,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaybackAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/blackout/on":{"post":{"operationId":"blackoutOn","summary":"Turn blackout on.","tags":["Control"],"x-scope":"control","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":false}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"blackoutOnByLink","summary":"Turn blackout on. A button link: the parameters in the query, for a key made for links. A HEAD answers 405, so a link checker never presses it.","tags":["Control"],"x-scope":"control","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/blackout/off":{"post":{"operationId":"blackoutOff","summary":"Turn blackout off.","tags":["Control"],"x-scope":"control","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":false}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"blackoutOffByLink","summary":"Turn blackout off. A button link: the parameters in the query, for a key made for links. A HEAD answers 405, so a link checker never presses it.","tags":["Control"],"x-scope":"control","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/blackout/toggle":{"post":{"operationId":"blackoutToggle","summary":"Turn blackout on or off.","tags":["Control"],"x-scope":"control","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":false}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"blackoutToggleByLink","summary":"Turn blackout on or off. A button link: the parameters in the query, for a key made for links. A HEAD answers 405, so a link checker never presses it.","tags":["Control"],"x-scope":"control","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/flash":{"post":{"operationId":"flash","summary":"Flash the screens or the message.","tags":["Control"],"x-scope":"control","parameters":[{"$ref":"#/components/parameters/RoomId"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer","minimum":1,"maximum":999},"target":{"enum":["screen","message"]}},"additionalProperties":false}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlashAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"flashByLink","summary":"Flash the screens or the message. A button link: the parameters in the query, for a key made for links. A HEAD answers 405, so a link checker never presses it.","tags":["Control"],"x-scope":"control","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"name":"count","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":999}},{"name":"target","in":"query","required":false,"schema":{"enum":["screen","message"]}}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlashAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/messages/show":{"post":{"operationId":"showMessage","summary":"Show a message, by its id or its place. Give exactly one of messageId or index. A write that changes the rundown or the messages counts as one write per 100 items the room holds, at least one.","tags":["Control"],"x-scope":"control","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"type":"object","properties":{"messageId":{"type":"string","minLength":1,"maxLength":64},"index":{"type":"integer","minimum":1,"maximum":1000,"description":"The message's place in the controller's list."},"focus":{"type":"boolean"}},"additionalProperties":false,"oneOf":[{"required":["messageId"]},{"required":["index"]}]}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessagesAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"showMessageByLink","summary":"Show a message, by its id or its place. Give exactly one of messageId or index. A write that changes the rundown or the messages counts as one write per 100 items the room holds, at least one. A button link: the parameters in the query, for a key made for links. A HEAD answers 405, so a link checker never presses it.","tags":["Control"],"x-scope":"control","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"messageId","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":64}},{"name":"index","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":1000,"description":"The message's place in the controller's list."}},{"name":"focus","in":"query","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessagesAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/messages/toggle":{"post":{"operationId":"toggleMessage","summary":"Show or hide a message, by its id or its place. Give exactly one of messageId or index. A write that changes the rundown or the messages counts as one write per 100 items the room holds, at least one.","tags":["Control"],"x-scope":"control","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"type":"object","properties":{"messageId":{"type":"string","minLength":1,"maxLength":64},"index":{"type":"integer","minimum":1,"maximum":1000,"description":"The message's place in the controller's list."},"focus":{"type":"boolean"}},"additionalProperties":false,"oneOf":[{"required":["messageId"]},{"required":["index"]}]}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessagesAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"toggleMessageByLink","summary":"Show or hide a message, by its id or its place. Give exactly one of messageId or index. A write that changes the rundown or the messages counts as one write per 100 items the room holds, at least one. A button link: the parameters in the query, for a key made for links. A HEAD answers 405, so a link checker never presses it.","tags":["Control"],"x-scope":"control","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"messageId","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":64}},{"name":"index","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":1000,"description":"The message's place in the controller's list."}},{"name":"focus","in":"query","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessagesAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/messages/hide":{"post":{"operationId":"hideMessage","summary":"Hide a message, by its id or its place. Give exactly one of messageId or index. A write that changes the rundown or the messages counts as one write per 100 items the room holds, at least one.","tags":["Control"],"x-scope":"control","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":true,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"type":"object","properties":{"messageId":{"type":"string","minLength":1,"maxLength":64},"index":{"type":"integer","minimum":1,"maximum":1000,"description":"The message's place in the controller's list."}},"additionalProperties":false,"oneOf":[{"required":["messageId"]},{"required":["index"]}]}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessagesAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"hideMessageByLink","summary":"Hide a message, by its id or its place. Give exactly one of messageId or index. A write that changes the rundown or the messages counts as one write per 100 items the room holds, at least one. A button link: the parameters in the query, for a key made for links. A HEAD answers 405, so a link checker never presses it.","tags":["Control"],"x-scope":"control","security":[{"bearerKey":[]},{"linkKey":[]}],"parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"},{"name":"messageId","in":"query","required":false,"schema":{"type":"string","minLength":1,"maxLength":64}},{"name":"index","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":1000,"description":"The message's place in the controller's list."}}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessagesAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/timers/reorder":{"post":{"operationId":"reorderTimers","summary":"Put the rundown in a new order. A write that changes the rundown or the messages counts as one write per 100 items the room holds, at least one.","tags":["Edit"],"x-scope":"edit","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReorderBody"}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimersAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/timers/{timerId}":{"patch":{"operationId":"updateTimer","summary":"Change a timer. A write that changes the rundown or the messages counts as one write per 100 items the room holds, at least one.","tags":["Edit"],"x-scope":"edit","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/TimerId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimerBody"}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimersAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteTimer","summary":"Delete a timer. A write that changes the rundown or the messages counts as one write per 100 items the room holds, at least one.","tags":["Edit"],"x-scope":"edit","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/TimerId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimersAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/timers/{timerId}/duplicate":{"post":{"operationId":"duplicateTimer","summary":"Duplicate a timer, next to it. A write that changes the rundown or the messages counts as one write per 100 items the room holds, at least one.","tags":["Edit"],"x-scope":"edit","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/TimerId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnyObject"}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimersAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/messages/reorder":{"post":{"operationId":"reorderMessages","summary":"Put the messages in a new order. A write that changes the rundown or the messages counts as one write per 100 items the room holds, at least one.","tags":["Edit"],"x-scope":"edit","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReorderBody"}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessagesAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rooms/{roomId}/messages/{messageId}":{"patch":{"operationId":"updateMessage","summary":"Change a message. A write that changes the rundown or the messages counts as one write per 100 items the room holds, at least one.","tags":["Edit"],"x-scope":"edit","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/MessageId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"required":false,"description":"An empty body is read as {}.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageBody"}}}},"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessagesAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"deleteMessage","summary":"Delete a message. A write that changes the rundown or the messages counts as one write per 100 items the room holds, at least one.","tags":["Edit"],"x-scope":"edit","parameters":[{"$ref":"#/components/parameters/RoomId"},{"$ref":"#/components/parameters/MessageId"},{"$ref":"#/components/parameters/CommandId"},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"description":"The room route's acknowledgement.","headers":{"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left.","required":true,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":true,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":true,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessagesAck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/NoKey"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"components":{"securitySchemes":{"bearerKey":{"type":"http","scheme":"bearer","bearerFormat":"csk_ followed by 43 base64url characters","description":"Send the key in Authorization: Bearer csk_… on any method."},"linkKey":{"type":"apiKey","in":"query","name":"api_key","description":"Only for a key made for links, and only on a control command's GET and on the stream. A link holding a key is a password."}},"parameters":{"RoomId":{"name":"roomId","in":"path","required":true,"description":"The room's id as the app shows it, without its hyphen. It is also read in lower case, with a hyphen or spaces, and with O, I, and L as 0, 1, and 1.","schema":{"type":"string","pattern":"^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{10}$"}},"TimerId":{"name":"timerId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"MessageId":{"name":"messageId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"CommandId":{"name":"Command-Id","in":"header","required":false,"description":"A UUID. A repeat of a command that succeeded replays its answer; the same id with another body, or on another route, answers 409 COMMAND_ID_REUSED, and one another caller used answers 403 COMMAND_ID_FOREIGN. A refused command keeps no receipt, so its id may be sent again. Flash reads none.","schema":{"type":"string","format":"uuid"}},"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"The same as Command-Id; both, with different ids, answer 400 VALIDATION. Flash reads neither.","schema":{"type":"string","format":"uuid"}},"IfNoneMatch":{"name":"If-None-Match","in":"header","required":false,"description":"The ETag of the room's revision the caller holds: 304 when unchanged.","schema":{"type":"string"}}},"responses":{"BadRequest":{"description":"VALIDATION, INVALID_AMOUNT, KEY_IN_URL (a key in a query where none may go, or from a key not made for links), or KEY_CONFLICT (a key in the header and the query).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"x-codes":["VALIDATION","INVALID_AMOUNT","KEY_IN_URL","KEY_CONFLICT"]},"NoKey":{"description":"KEY_REQUIRED, or KEY_INVALID: unknown, revoked, or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"x-codes":["KEY_REQUIRED","KEY_INVALID"]},"Forbidden":{"description":"SCOPE_FORBIDDEN, KEY_DISABLED (beyond the plan's count, with limit and plan), ROOM_DISABLED, API_OFF, NOT_A_PRESS (a prefetch, a prerender, or a link preview), PLAN_LIMIT (with limit, used, and plan), TIMER_DISABLED, or COMMAND_ID_FOREIGN (a command id another caller used).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"x-codes":["SCOPE_FORBIDDEN","KEY_DISABLED","ROOM_DISABLED","API_OFF","NOT_A_PRESS","PLAN_LIMIT","TIMER_DISABLED","COMMAND_ID_FOREIGN"]},"NotFound":{"description":"ROOM_NOT_FOUND (also a room outside the key's reach), TIMER_NOT_FOUND, MESSAGE_NOT_FOUND, or NOT_FOUND (an id that is not a UUID).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"x-codes":["ROOM_NOT_FOUND","TIMER_NOT_FOUND","MESSAGE_NOT_FOUND","NOT_FOUND"]},"Conflict":{"description":"REVISION_CONFLICT (ifRevision is not the room's revision), COMMAND_ID_REUSED (a command id sent again with another body), or LIMIT_REACHED (a room's most timers or messages).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"x-codes":["REVISION_CONFLICT","COMMAND_ID_REUSED","LIMIT_REACHED"]},"TooLarge":{"description":"TOO_LARGE: a body over 64 KB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"x-codes":["TOO_LARGE"]},"RateLimited":{"description":"RATE_LIMITED, or STREAM_LIMIT: wait for Retry-After.","headers":{"Retry-After":{"description":"Seconds until one request is free.","required":true,"schema":{"type":"integer"}},"RateLimit-Limit":{"description":"The burst of the key's or the account's bucket, whichever has fewer requests left. Only when a live key's bucket refused.","required":false,"schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Whole requests left in that bucket.","required":false,"schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until that bucket is full again. A refused request waits for Retry-After instead.","required":false,"schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"x-codes":["RATE_LIMITED","STREAM_LIMIT"]}},"schemas":{"Error":{"type":"object","description":"Every refusal: a code, a message, and for some codes their numbers.","properties":{"ok":{"const":false},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"limit":{"type":["integer","null"]},"used":{"type":"integer"},"plan":{"type":"string"}},"required":["code","message"],"additionalProperties":true}},"required":["ok","error"],"additionalProperties":false},"RoomListItem":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"disabled":{"type":"boolean","description":"The plan disables the room."},"apiOff":{"type":"boolean","description":"The room's controller turned the API off."}},"required":["id","name","disabled","apiOff"],"additionalProperties":false},"RoomSettings":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"timezone":{"type":"string","description":"An IANA time zone name."},"clockFormat":{"enum":["12h","24h"]},"blackout":{"type":"boolean"},"messageFocus":{"type":"boolean"},"defaults":{"type":"object","properties":{"wrapUpYellowMs":{"type":"integer","minimum":0,"maximum":86400000},"wrapUpRedMs":{"type":"integer","minimum":0,"maximum":86400000}},"required":["wrapUpYellowMs","wrapUpRedMs"],"additionalProperties":false},"overtime":{"type":"object","properties":{"mode":{"enum":["continue","stop"]},"sign":{"enum":["minus","plus"]}},"required":["mode","sign"],"additionalProperties":false}},"required":["id","name","timezone","clockFormat","blackout","messageFocus","defaults","overtime"],"additionalProperties":false},"Room":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"timezone":{"type":"string"},"clockFormat":{"enum":["12h","24h"]},"blackout":{"type":"boolean"},"onAir":{"type":"boolean"},"messageFocus":{"type":"boolean"},"defaults":{"type":"object","properties":{"wrapUpYellowMs":{"type":"integer","minimum":0,"maximum":86400000},"wrapUpRedMs":{"type":"integer","minimum":0,"maximum":86400000}},"required":["wrapUpYellowMs","wrapUpRedMs"],"additionalProperties":false},"overtime":{"type":"object","properties":{"mode":{"enum":["continue","stop"]},"sign":{"enum":["minus","plus"]}},"required":["mode","sign"],"additionalProperties":false},"eventId":{"type":["string","null"]},"revision":{"type":"integer","minimum":0},"lastActivityAt":{"type":"integer","minimum":0,"maximum":9007199254740991},"createdAt":{"type":"integer","minimum":0,"maximum":9007199254740991},"updatedAt":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["id","name","timezone","clockFormat","blackout","onAir","messageFocus","defaults","overtime","eventId","revision","lastActivityAt","createdAt","updatedAt"],"additionalProperties":false,"description":"The room as a write's acknowledgement carries it."},"Timer":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"roomId":{"type":"string"},"position":{"type":"integer","minimum":0},"name":{"type":"string"},"speaker":{"type":"string"},"notes":{"type":"string"},"durationMs":{"type":"integer","minimum":1000,"maximum":86400000},"appearance":{"enum":["COUNTDOWN","COUNTUP","TOD","COUNTDOWN_TOD","COUNTUP_TOD","HIDDEN"]},"type":{"enum":["DURATION","FINISH_TIME","TIME_WARP"]},"finishAt":{"type":["integer","null"]},"presenterDurationMs":{"type":["integer","null"]},"trigger":{"enum":["MANUAL","LINKED","SCHEDULED"]},"scheduledStartAt":{"type":["integer","null"]},"day":{"type":["string","null"]},"wrapUpYellowMs":{"type":"integer","minimum":0,"maximum":86400000},"wrapUpRedMs":{"type":"integer","minimum":0,"maximum":86400000},"labels":{"type":"array","items":{"type":"string"},"maxItems":8},"actions":{"type":"object","properties":{"start":{"type":"object","properties":{"chime":{"enum":["none","soft","bell","alarm"]},"flash":{"type":"integer","minimum":0,"maximum":5}},"required":["chime","flash"],"additionalProperties":false},"yellow":{"type":"object","properties":{"chime":{"enum":["none","soft","bell","alarm"]},"flash":{"type":"integer","minimum":0,"maximum":5}},"required":["chime","flash"],"additionalProperties":false},"red":{"type":"object","properties":{"chime":{"enum":["none","soft","bell","alarm"]},"flash":{"type":"integer","minimum":0,"maximum":5}},"required":["chime","flash"],"additionalProperties":false},"zero":{"type":"object","properties":{"chime":{"enum":["none","soft","bell","alarm"]},"flash":{"type":"integer","minimum":0,"maximum":5}},"required":["chime","flash"],"additionalProperties":false}},"required":["start","yellow","red","zero"],"additionalProperties":false},"createdAt":{"type":"integer","minimum":0,"maximum":9007199254740991},"updatedAt":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["id","roomId","position","name","speaker","notes","durationMs","appearance","type","finishAt","presenterDurationMs","trigger","scheduledStartAt","day","wrapUpYellowMs","wrapUpRedMs","labels","actions","createdAt","updatedAt"],"additionalProperties":false},"Message":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"roomId":{"type":"string"},"position":{"type":"integer","minimum":0},"text":{"type":"string"},"color":{"enum":["white","green","red"]},"bold":{"type":"boolean"},"uppercase":{"type":"boolean"},"showing":{"type":"boolean"},"createdAt":{"type":"integer","minimum":0,"maximum":9007199254740991},"updatedAt":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["id","roomId","position","text","color","bold","uppercase","showing","createdAt","updatedAt"],"additionalProperties":false},"Playback":{"type":"object","properties":{"timerId":{"type":["string","null"]},"running":{"type":"boolean"},"startAt":{"type":["integer","null"]},"finishAt":{"type":["integer","null"]},"pauseAt":{"type":["integer","null"]}},"required":["timerId","running","startAt","finishAt","pauseAt"],"additionalProperties":false},"TimerRef":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"index":{"type":"integer","minimum":1,"description":"The number the controller prints."},"name":{"type":"string"},"speaker":{"type":"string"}},"required":["id","index","name","speaker"],"additionalProperties":false},"NextTimer":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"index":{"type":"integer","minimum":1},"name":{"type":"string"},"speaker":{"type":"string"},"disabled":{"type":"boolean","description":"The plan disables the timer next would cue."}},"required":["id","index","name","speaker","disabled"],"additionalProperties":false},"Status":{"type":"object","properties":{"ok":{"const":true},"revision":{"type":"integer","minimum":0},"serverTime":{"type":"integer","minimum":0,"maximum":9007199254740991},"room":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"blackout":{"type":"boolean"}},"required":["id","name","blackout"],"additionalProperties":false},"timer":{"oneOf":[{"$ref":"#/components/schemas/TimerRef"},{"type":"null"}]},"next":{"oneOf":[{"$ref":"#/components/schemas/NextTimer"},{"type":"null"}]},"running":{"type":"boolean"},"phase":{"enum":["green","yellow","red","overtime","idle"]},"remainingMs":{"type":["integer","null"]},"elapsedMs":{"type":["integer","null"]},"totalMs":{"type":["integer","null"]},"message":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"text":{"type":"string"},"color":{"enum":["white","green","red"]},"focus":{"type":"boolean"}},"required":["id","text","color","focus"],"additionalProperties":false},{"type":"null"}]}},"required":["ok","revision","serverTime","room","timer","next","running","phase","remainingMs","elapsedMs","totalMs","message"],"additionalProperties":false,"description":"The loaded timer and the one next would cue, with the times the presenter sees, the phase, blackout, and the message showing. Always 200: its times move at the same revision."},"PlaybackAck":{"type":"object","properties":{"ok":{"const":true},"changed":{"type":"boolean","description":"False when the command changed nothing."},"revision":{"type":"integer","minimum":0},"playback":{"$ref":"#/components/schemas/Playback"},"replayed":{"const":true,"description":"A repeat of a command id, answered from its receipt."},"truncated":{"const":true,"description":"A replayed answer over 64 KB: the envelope alone, without its section."}},"required":["ok","changed","revision"],"additionalProperties":false,"oneOf":[{"required":["playback"],"not":{"required":["truncated"]}},{"required":["truncated"]}]},"RoomAck":{"type":"object","properties":{"ok":{"const":true},"changed":{"type":"boolean","description":"False when the command changed nothing."},"revision":{"type":"integer","minimum":0},"room":{"$ref":"#/components/schemas/Room"},"replayed":{"const":true,"description":"A repeat of a command id, answered from its receipt."},"truncated":{"const":true,"description":"A replayed answer over 64 KB: the envelope alone, without its section."}},"required":["ok","changed","revision"],"additionalProperties":false,"oneOf":[{"required":["room"],"not":{"required":["truncated"]}},{"required":["truncated"]}]},"TimersAck":{"type":"object","properties":{"ok":{"const":true},"changed":{"type":"boolean","description":"False when the command changed nothing."},"revision":{"type":"integer","minimum":0},"timers":{"type":"array","items":{"$ref":"#/components/schemas/Timer"}},"playback":{"$ref":"#/components/schemas/Playback"},"replayed":{"const":true,"description":"A repeat of a command id, answered from its receipt."},"truncated":{"const":true,"description":"A replayed answer over 64 KB: the envelope alone, without its section."}},"required":["ok","changed","revision"],"additionalProperties":false,"oneOf":[{"required":["timers","playback"],"not":{"required":["truncated"]}},{"required":["truncated"]}]},"MessagesAck":{"type":"object","properties":{"ok":{"const":true},"changed":{"type":"boolean","description":"False when the command changed nothing."},"revision":{"type":"integer","minimum":0},"messages":{"type":"array","items":{"$ref":"#/components/schemas/Message"}},"room":{"$ref":"#/components/schemas/Room"},"replayed":{"const":true,"description":"A repeat of a command id, answered from its receipt."},"truncated":{"const":true,"description":"A replayed answer over 64 KB: the envelope alone, without its section."}},"required":["ok","changed","revision"],"additionalProperties":false,"oneOf":[{"required":["messages","room"],"not":{"required":["truncated"]}},{"required":["truncated"]}]},"RoomUpdate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"Trimmed before its length is checked."},"timezone":{"type":"string","description":"An IANA time zone name."},"clockFormat":{"enum":["12h","24h"]},"defaults":{"type":"object","properties":{"wrapUpYellowMs":{"type":"integer","minimum":0,"maximum":86400000},"wrapUpRedMs":{"type":"integer","minimum":0,"maximum":86400000}},"required":["wrapUpYellowMs","wrapUpRedMs"],"additionalProperties":false,"description":"wrapUpYellowMs must be greater than or equal to wrapUpRedMs."},"overtime":{"type":"object","properties":{"mode":{"const":"continue"},"sign":{"const":"minus"}},"required":["mode","sign"],"additionalProperties":false,"description":"Only continue and minus today; stop and plus are a later release."}},"additionalProperties":false},"TimerBody":{"type":"object","properties":{"name":{"type":"string","maxLength":200,"description":"Trimmed before its length is checked."},"speaker":{"type":"string","maxLength":200,"description":"Trimmed before its length is checked."},"notes":{"type":"string","maxLength":5000},"labels":{"type":"array","items":{"type":"string","minLength":1,"maxLength":32},"maxItems":8,"description":"Each label trimmed before its length is checked, and kept once."},"durationMs":{"type":"integer","minimum":1000,"maximum":86400000},"type":{"enum":["DURATION","FINISH_TIME","TIME_WARP"]},"finishAt":{"type":["integer","null"],"minimum":0,"maximum":9007199254740991},"presenterDurationMs":{"type":["integer","null"],"minimum":1000,"maximum":86400000},"appearance":{"enum":["COUNTDOWN","COUNTUP","TOD","COUNTDOWN_TOD","COUNTUP_TOD","HIDDEN"]},"wrapUpYellowMs":{"type":"integer","minimum":0,"maximum":86400000},"wrapUpRedMs":{"type":"integer","minimum":0,"maximum":86400000},"trigger":{"enum":["MANUAL","LINKED","SCHEDULED"]},"scheduledStartAt":{"type":["integer","null"],"minimum":0,"maximum":9007199254740991},"day":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$","examples":["2026-10-27"],"description":"A calendar date, YYYY-MM-DD."},"actions":{"type":"object","properties":{"start":{"type":"object","properties":{"chime":{"enum":["none","soft","bell","alarm"]},"flash":{"type":"integer","minimum":0,"maximum":5}},"additionalProperties":false},"yellow":{"type":"object","properties":{"chime":{"enum":["none","soft","bell","alarm"]},"flash":{"type":"integer","minimum":0,"maximum":5}},"additionalProperties":false},"red":{"type":"object","properties":{"chime":{"enum":["none","soft","bell","alarm"]},"flash":{"type":"integer","minimum":0,"maximum":5}},"additionalProperties":false},"zero":{"type":"object","properties":{"chime":{"enum":["none","soft","bell","alarm"]},"flash":{"type":"integer","minimum":0,"maximum":5}},"additionalProperties":false}},"additionalProperties":false},"position":{"type":"integer","minimum":0,"maximum":500}},"additionalProperties":false,"description":"A timer's fields; a change sends only those it changes. On the timer as it would be: a finish-time timer needs finishAt, a time-warp timer presenterDurationMs, and a scheduled one scheduledStartAt; wrapUpYellowMs is at or above wrapUpRedMs; a scheduled start is no more than a day past, and a scheduled timer's day is its start's date in the room's time zone."},"ReorderBody":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64},"minItems":1,"maxItems":500,"description":"Every id of the list, each once, in the new order."}},"required":["ids"],"additionalProperties":false},"MessageBody":{"type":"object","properties":{"text":{"type":"string","maxLength":1000},"color":{"enum":["white","green","red"]},"bold":{"type":"boolean"},"uppercase":{"type":"boolean"},"position":{"type":"integer","minimum":0,"maximum":500}},"additionalProperties":false},"FlashAck":{"type":"object","properties":{"ok":{"const":true}},"required":["ok"],"additionalProperties":false},"AnyObject":{"type":"object","description":"A JSON object whose fields are ignored; it still counts in a command id's identity."}}}}