{"openapi":"3.1.0","info":{"title":"Gleem Developer API","version":"1.0.0","description":"Read and act on a creator's Gleem data with a personal access token or an OAuth 2 access token. Real-time events arrive over the WebSocket; see https://gleem.gg/developers/websocket. OAuth apps: see https://gleem.gg/developers/oauth. Within v1, changes are only additive."},"servers":[{"url":"https://gleem.gg/api/v1"}],"security":[{"bearerAuth":[]},{"oauth2":[]}],"tags":[{"name":"Account","description":"The token and the creator it belongs to."},{"name":"Events","description":"Authorising the real-time event stream."},{"name":"Queue","description":"The Treat queue."},{"name":"Treats","description":"The creator's Treats."},{"name":"Gems","description":"Gem messages and the Gem leaderboard."},{"name":"Memberships","description":"Membership tiers."},{"name":"Tickets","description":"Trigger Tickets."},{"name":"IRL","description":"Gleem IRL devices (IRL Sidekicks and phones running the Gleem IRL app), how their streams are doing, pairing and remote control."},{"name":"Hosting","description":"OBS Server Hosting rentals the creator has made."},{"name":"Stream Manager","description":"The creator's stream on Twitch (and more platforms over time): status, title and category, chat and moderation as the streamer, recent chat and activity, and the Gleem IRL app's Quick Actions layout."},{"name":"Webhooks","description":"Webhooks an OAuth app registers for a creator it holds a grant for. Needs the `webhooks:write` scope, which only OAuth apps can be granted."}],"paths":{"/me":{"get":{"operationId":"getMe","summary":"The token's creator, scopes and event stream","tags":["Account"],"responses":{"200":{"description":"The creator and token. With an OAuth token also the app; with an app token only the app.","content":{"application/json":{"schema":{"type":"object","properties":{"creator":{"type":"object","properties":{"id":{"type":"integer"},"slug":{"type":"string"},"display_name":{"type":"string"},"twitch_id":{"type":["string","null"]},"is_live":{"type":"boolean"},"url":{"type":"string"}}},"token":{"type":"object","properties":{"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"expires_at":{"type":["string","null"],"format":"date-time"}}},"websocket":{"oneOf":[{"type":"object","properties":{"url":{"type":"string"},"channel":{"type":"string"},"auth_url":{"type":"string"}}},{"type":"null"}]},"app":{"type":"object","description":"Only with an OAuth token: the app it was issued to. With an app token (client credentials) this is the whole response.","properties":{"id":{"type":"string","description":"The app's client id, `gla_…`."},"name":{"type":"string"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"description":"Needs no scope."}},"/socket/auth":{"post":{"operationId":"authorizeSocket","summary":"Sign a WebSocket channel subscription","tags":["Events"],"responses":{"200":{"description":"Pusher channel authorisation","content":{"application/json":{"schema":{"type":"object","properties":{"auth":{"type":"string"},"channel_name":{"type":"string"}}}}}},"422":{"description":"Malformed socket_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"events:read,irl:read,stream:read","description":"Pusher channel authorisation for one of the token's own channels: the creator's event channel (the default; needs `events:read`), `private-irl-device.{uuid}.telemetry` for one of the creator's own IRL devices (needs `irl:read` and Gleem IRL on the account; carries no network addresses), `private-irl-device.{uuid}.network`, the same with the device's network addresses (also needs `irl:network`; refused to tokens issued for a rented OBS machine), or the Stream Manager channel `private-creator.{id}.stream` (needs `stream:read` and Gleem IRL on the account). Without Gleem IRL those two answer `403` with the code `feature_unavailable`. Pusher client libraries may send the same fields form-encoded.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["socket_id"],"properties":{"socket_id":{"type":"string","pattern":"^\\d+\\.\\d+$"},"channel_name":{"type":"string","description":"Defaults to the creator's event channel. The only other channels a token may sign are its own IRL devices' telemetry channels and its Stream Manager channel."}}}}}}}},"/queue":{"get":{"operationId":"getQueue","summary":"The open queue and what is still owed","tags":["Queue"],"responses":{"200":{"description":"The queue","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"oneOf":[{"$ref":"#/components/schemas/QueueSession"},{"type":"null"}]},"entries":{"type":"array","items":{"$ref":"#/components/schemas/QueueEntry"}},"leftovers":{"type":"array","items":{"$ref":"#/components/schemas/QueueEntry"},"description":"Pending or active entries from queues that have closed."}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"queue:read","description":"Requires the `queue:read` scope."}},"/queue/open":{"post":{"operationId":"openQueue","summary":"Open a queue","tags":["Queue"],"responses":{"201":{"description":"The new queue","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"$ref":"#/components/schemas/QueueSession"}}}}}},"422":{"description":"Invalid settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"queue:write","description":"A queue that is already open is closed first. Requires the `queue:write` scope.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":[],"properties":{"total_slots":{"type":["integer","null"],"minimum":1,"maximum":100,"description":"Defaults to the size used last, or 10 for a first queue."},"closes_at":{"type":["string","null"],"pattern":"^\\d{2}:\\d{2}$","description":"Close automatically at this time."},"timezone":{"type":["string","null"],"description":"IANA timezone for closes_at. Defaults to the one used last."}}}}}}}},"/queue/close":{"post":{"operationId":"closeQueue","summary":"Close the queue","tags":["Queue"],"responses":{"200":{"description":"The closed queue, or null when none was open","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"oneOf":[{"$ref":"#/components/schemas/QueueSession"},{"type":"null"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"queue:write","description":"Requires the `queue:write` scope."}},"/queue/entries/{queueEntry}":{"patch":{"operationId":"updateQueueEntry","summary":"Change an entry's status","tags":["Queue"],"responses":{"200":{"description":"The entry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueueEntry"}}}},"404":{"description":"No such entry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"queue:write","description":"Requires the `queue:write` scope.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["pending","active","completed"]}}}}}},"parameters":[{"name":"queueEntry","in":"path","required":true,"schema":{"type":"integer"},"description":"The queue entry id."}]}},"/queue/entries/{queueEntry}/refund":{"post":{"operationId":"refundQueueEntry","summary":"Refund and cancel an entry","tags":["Queue"],"responses":{"200":{"description":"The cancelled entry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueueEntry"}}}},"404":{"description":"No such entry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Already refunded (`already_refunded`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"queue:write","description":"Gives back what was paid: Gems to the ledger they came from, or a Trigger Ticket. Requires the `queue:write` scope.","parameters":[{"name":"queueEntry","in":"path","required":true,"schema":{"type":"integer"},"description":"The queue entry id."}]}},"/treats":{"get":{"operationId":"listTreats","summary":"Every Treat, by category","tags":["Treats"],"responses":{"200":{"description":"Categories with their Treats, hidden ones included","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TreatCategory"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"treats:read","description":"Requires the `treats:read` scope."}},"/treats/{rewardItem}":{"patch":{"operationId":"updateTreat","summary":"Show, hide, pin or unpin a Treat","tags":["Treats"],"responses":{"200":{"description":"The Treat","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Treat"},{"type":"object","properties":{"channel_points_synced":{"type":"boolean","description":"False when Twitch did not follow along with a visibility change."}}}]}}}},"404":{"description":"No such Treat.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Neither field given.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"treats:write","description":"Requires the `treats:write` scope.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","minProperties":1,"properties":{"is_active":{"type":"boolean"},"is_pinned":{"type":"boolean"}}}}}},"parameters":[{"name":"rewardItem","in":"path","required":true,"schema":{"type":"integer"},"description":"The Treat id."}]}},"/tips":{"get":{"operationId":"listTips","summary":"Gem messages, newest first","tags":["Gems"],"responses":{"200":{"description":"A page of Gem messages","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Tip"}},"meta":{"type":"object","properties":{"current_page":{"type":"integer"},"last_page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"integer"}}},"links":{"type":"object","properties":{"next":{"type":["string","null"]},"prev":{"type":["string","null"]}}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"tips:read","description":"Requires the `tips:read` scope.","parameters":[{"name":"since","in":"query","schema":{"type":"string","format":"date-time"},"description":"Only messages sent at or after this time."},{"name":"per_page","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1}}]}},"/leaderboard":{"get":{"operationId":"getLeaderboard","summary":"Top 25 viewers by Gems spent","tags":["Gems"],"responses":{"200":{"description":"The ranking","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"rank":{"type":"integer"},"member":{"oneOf":[{"$ref":"#/components/schemas/Member"},{"type":"null"}]},"total_gems_spent":{"type":"integer"}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"tips:read","description":"Requires the `tips:read` scope."}},"/memberships":{"get":{"operationId":"listMemberships","summary":"Membership tiers and active members","tags":["Memberships"],"responses":{"200":{"description":"The tiers","content":{"application/json":{"schema":{"type":"object","properties":{"active_members":{"type":"integer"},"tiers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"description":{"type":["string","null"]},"price_gems":{"type":"integer"},"active_members":{"type":"integer"}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"members:read","description":"Requires the `members:read` scope."}},"/tickets/grant":{"post":{"operationId":"grantTickets","summary":"Grant Trigger Tickets to a viewer","tags":["Tickets"],"responses":{"200":{"description":"The grant and the viewer's new balance","content":{"application/json":{"schema":{"type":"object","properties":{"member":{"$ref":"#/components/schemas/Member"},"amount":{"type":"integer"},"balance":{"type":"integer"}}}}}},"422":{"description":"Invalid request, or no Twitch account has that login (`unknown_twitch_user`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"tickets:write","description":"Identify the viewer by `twitch_user_id` (preferred, no lookup) or `twitch_login`. A viewer new to Gleem gets an account. Requires the `tickets:write` scope.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["amount"],"properties":{"twitch_user_id":{"type":"string","pattern":"^\\d{1,20}$"},"twitch_login":{"type":"string","pattern":"^[a-zA-Z0-9_]{1,25}$"},"amount":{"type":"integer","minimum":1,"maximum":1000}}}}}}}},"/irl/devices":{"get":{"operationId":"listIrlDevices","summary":"Your IRL devices and their stream health","tags":["IRL"],"responses":{"200":{"description":"The devices, by name","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/IrlDevice"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"irl:read","description":"Requires the `irl:read` scope and Gleem IRL on the account; without it the answer is `403` with the code `feature_unavailable`."},"post":{"operationId":"registerIrlDevice","summary":"Register a phone as an IRL device","tags":["IRL"],"description":"For the Gleem IRL app: registers the phone it runs on as one of the creator's IRL devices, as if a pairing had been approved and collected in one step. The `gid_` credential in the answer is shown only here; the phone uses it for the device API (heartbeat, command results). Requires the `irl:write` scope and Gleem IRL on the account; without it the answer is `403` with the code `feature_unavailable`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","board"],"properties":{"name":{"type":"string","maxLength":64},"board":{"type":"string","enum":["android","ios"]},"hardware":{"type":["string","null"],"maxLength":128,"description":"The phone's model."},"app_version":{"type":["string","null"],"maxLength":32,"description":"The app's version. The heartbeat sends it again as `sidekick_version`."}}}}}},"responses":{"201":{"description":"The device, its credential and its command channel","content":{"application/json":{"schema":{"type":"object","properties":{"device":{"$ref":"#/components/schemas/IrlDevice"},"credential":{"type":"string","description":"`gid_…`. Shown once; store it in the phone's secure storage."},"realtime":{"$ref":"#/components/schemas/IrlRealtime"}}}}}},"422":{"description":"Invalid name or board.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"irl:write"}},"/irl/devices/{irlDevice}":{"get":{"operationId":"showIrlDevice","summary":"One IRL device and its stream health","tags":["IRL"],"parameters":[{"name":"irlDevice","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The device's uuid, from the list or its page in the dashboard."}],"responses":{"200":{"description":"The device","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IrlDevice"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"404":{"description":"No such device.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-gleem-scope":"irl:read","description":"Requires the `irl:read` scope and Gleem IRL on the account; without it the answer is `403` with the code `feature_unavailable`."},"patch":{"operationId":"renameIrlDevice","summary":"Rename an IRL device","tags":["IRL"],"parameters":[{"name":"irlDevice","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The device's uuid, from the list or its page in the dashboard."}],"description":"Requires the `irl:write` scope and Gleem IRL on the account; without it the answer is `403` with the code `feature_unavailable`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":64}}}}}},"responses":{"200":{"description":"The device","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IrlDevice"}}}},"404":{"description":"No such device.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid name.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"irl:write"},"delete":{"operationId":"deleteIrlDevice","summary":"Remove an IRL device","tags":["IRL"],"parameters":[{"name":"irlDevice","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The device's uuid, from the list or its page in the dashboard."}],"description":"Removes the device and its credential: its next request is refused and it shows itself unpaired. Its stream history stays. Requires the `irl:write` scope and Gleem IRL on the account; without it the answer is `403` with the code `feature_unavailable`.","responses":{"204":{"description":"Removed."},"404":{"description":"No such device.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"irl:write"}},"/irl/devices/{irlDevice}/playback":{"get":{"operationId":"showIrlDevicePlayback","summary":"The SRT URL to watch an IRL device's stream in OBS","tags":["IRL"],"parameters":[{"name":"irlDevice","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The device's uuid, from the list or its page in the dashboard."}],"responses":{"200":{"description":"Where to pull the stream from","content":{"application/json":{"schema":{"type":"object","required":["url","protocol","region","latency_ms","key_version"],"properties":{"url":{"type":"string","description":"An SRT URL for an OBS Media Source or any SRT reader. A secret: anyone holding it can watch the stream."},"protocol":{"type":"string","enum":["srt"]},"region":{"type":"string","description":"The ingest region serving the stream, e.g. fsn1."},"latency_ms":{"type":"integer","description":"The SRT receive latency already set in the URL."},"key_version":{"type":"integer","description":"Goes up each time the creator asks for a new URL; the old one stops working within 10 seconds."}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No such device.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"No ingest server can serve the device right now (`playback_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"irl:read","description":"Requires the `irl:read` scope and Gleem IRL on the account; without it the answer is `403` with the code `feature_unavailable`. The URL is the one the device's dashboard page shows. It does not expire, but changes when the creator asks for a new one, so fetch it again when a pull is refused instead of storing it."}},"/irl/devices/{irlDevice}/commands":{"post":{"operationId":"sendIrlCommand","summary":"Start, stop, restart or update a device","tags":["IRL"],"parameters":[{"name":"irlDevice","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The device's uuid, from the list or its page in the dashboard."}],"description":"Sends a command to an online device over its command channel; it is also repeated in the device's heartbeats until answered, and expires unrun after 60 seconds. Nothing is queued for an offline device. `update.install` is refused while the device streams, while an install runs, or when its last heartbeat offered no update. Requires the `irl:write` scope and Gleem IRL on the account; without it the answer is `403` with the code `feature_unavailable`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["kind"],"properties":{"kind":{"type":"string","enum":["stream.start","stream.stop","restart","update.install"]},"target":{"type":"string","enum":["stream","sidekick","device"],"description":"Required for `restart`: the stream, the app or service, or the whole device."}}}}}},"responses":{"202":{"description":"The command, sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IrlCommand"}}}},"404":{"description":"No such device.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`device_offline`: the device is offline.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Unknown kind or target, or an update that cannot be installed now.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"irl:write"}},"/irl/devices/{irlDevice}/bitrate":{"put":{"operationId":"setIrlBitrate","summary":"Move a device's bitrate window","tags":["IRL"],"parameters":[{"name":"irlDevice","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"The device's uuid, from the list or its page in the dashboard."}],"description":"An online device gets the window at once and applies it to a running stream (`delivery: command`); an offline one gets it as a new settings revision with its next heartbeat (`delivery: next_heartbeat`). Requires the `irl:write` scope and Gleem IRL on the account; without it the answer is `403` with the code `feature_unavailable`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["min_kbps","max_kbps"],"properties":{"min_kbps":{"type":"integer","minimum":500,"description":"At most `max_kbps`."},"max_kbps":{"type":"integer","maximum":50000}}}}}},"responses":{"200":{"description":"The window and how it reaches the device","content":{"application/json":{"schema":{"type":"object","properties":{"min_kbps":{"type":"integer"},"max_kbps":{"type":"integer"},"delivery":{"type":"string","enum":["command","next_heartbeat"]},"command":{"oneOf":[{"$ref":"#/components/schemas/IrlCommand"},{"type":"null"}]},"settings_revision":{"type":"integer"}}}}}},"404":{"description":"No such device.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"A window outside 500–50000 kbit/s, or `min_kbps` above `max_kbps`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"irl:write"}},"/irl/pair/{code}":{"get":{"operationId":"showIrlPairing","summary":"The IRL Sidekick waiting under a pairing code","tags":["IRL"],"description":"What the pairing page on the dashboard shows before the creator approves: which device is waiting under the code and until when. Looking a code up does not answer it. Requires the `irl:write` scope and Gleem IRL on the account; without it the answer is `403` with the code `feature_unavailable`.","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","maxLength":32},"description":"The code the IRL Sidekick shows, such as `GLEEM-7K3M-Q9TX`, with or without the prefix and dashes, in any case."}],"responses":{"200":{"description":"The pairing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IrlPairing"}}}},"404":{"description":"`pairing_not_found`: no pairing is waiting for this code; it expired or was answered already.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"irl:write"}},"/irl/pair/approve":{"post":{"operationId":"approveIrlPairing","summary":"Approve an IRL Sidekick's pairing","tags":["IRL"],"description":"What the pairing page on the dashboard does: the IRL Sidekick showing the code becomes the creator's, and collects its credential with its next poll. Requires the `irl:write` scope and Gleem IRL on the account; without it the answer is `403` with the code `feature_unavailable`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code"],"properties":{"code":{"type":"string","maxLength":32,"description":"The code the IRL Sidekick shows, such as `GLEEM-7K3M-Q9TX`, with or without the prefix and dashes, in any case."},"name":{"type":["string","null"],"maxLength":64,"description":"Defaults to the name the device asked for."}}}}}},"responses":{"201":{"description":"The new device","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IrlDevice"}}}},"404":{"description":"`pairing_not_found`: no pairing is waiting for this code; it expired or was answered already.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"No code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"irl:write"}},"/irl/pair/decline":{"post":{"operationId":"declineIrlPairing","summary":"Decline an IRL Sidekick's pairing","tags":["IRL"],"description":"The device is told its pairing was declined. Requires the `irl:write` scope and Gleem IRL on the account; without it the answer is `403` with the code `feature_unavailable`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code"],"properties":{"code":{"type":"string","maxLength":32,"description":"The code the IRL Sidekick shows, such as `GLEEM-7K3M-Q9TX`, with or without the prefix and dashes, in any case."}}}}}},"responses":{"204":{"description":"Declined."},"404":{"description":"`pairing_not_found`: no pairing is waiting for this code; it expired or was answered already.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"No code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"irl:write"}},"/hosting/rentals":{"get":{"operationId":"listRentals","summary":"Your OBS rentals","tags":["Hosting"],"description":"Requires the `hosting:read` scope and OBS Server Hosting on the account; without it the answer is `403` with the code `feature_unavailable`. Only the renter sees a rental. Starting and ending rentals will need `hosting:write`, which is reserved.","parameters":[{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"A page of rentals, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Rental"}},"meta":{"type":"object","properties":{"current_page":{"type":"integer"},"last_page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"integer"}}},"links":{"type":"object","properties":{"next":{"type":["string","null"]},"prev":{"type":["string","null"]}}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"hosting:read"}},"/hosting/rentals/{rental}":{"get":{"operationId":"showRental","summary":"One OBS rental","tags":["Hosting"],"description":"Requires the `hosting:read` scope and OBS Server Hosting on the account; without it the answer is `403` with the code `feature_unavailable`. Only the renter sees a rental.","parameters":[{"name":"rental","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The rental","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Rental"}}}},"404":{"description":"No such rental.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"hosting:read"}},"/stream":{"get":{"operationId":"showStream","summary":"The creator's stream on every platform","tags":["Stream Manager"],"description":"Live, viewers, title and category on each streaming platform, what each connection allows, and where the Stream Manager's live channel is. Cached for about 15 seconds. Part of the Gleem IRL beta: without Gleem IRL on the account the answer is `403` with the code `feature_unavailable`.","responses":{"200":{"description":"Every platform","content":{"application/json":{"schema":{"type":"object","properties":{"platforms":{"type":"array","items":{"$ref":"#/components/schemas/StreamPlatformStatus"}},"socket":{"type":"object","properties":{"url":{"type":"string"},"channel":{"type":"string"},"auth_url":{"type":"string","format":"uri"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"stream:read"}},"/stream/{platform}":{"patch":{"operationId":"updateStreamInfo","summary":"Change the title and category","tags":["Stream Manager"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["twitch"]},"description":"The streaming platform. Only `twitch` for now; others are added over time."}],"description":"As the streamer. A null `category_id` clears the category. Answers with the platform's status. Part of the Gleem IRL beta: without Gleem IRL on the account the answer is `403` with the code `feature_unavailable`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","maxLength":140},"category_id":{"type":["string","null"]}}}}}},"responses":{"200":{"description":"The platform's status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StreamPlatformStatus"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"description":"The token lacks the scope (`insufficient_scope`), or the platform connection lacks a scope it needs and has to be renewed (`platform_reconnect_required`, with `platform`, `missing_scopes` and `reconnect_url`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"404":{"description":"Unknown platform.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The platform is not connected to this creator (`platform_not_connected`, with `reconnect_url`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid input (`validation_failed`), or the platform refused (`platform_rejected`, with `platform_status` and `platform_message`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The platform could not be reached (`platform_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-gleem-scope":"stream:write"}},"/stream/{platform}/categories":{"get":{"operationId":"searchStreamCategories","summary":"Search categories","tags":["Stream Manager"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["twitch"]},"description":"The streaming platform. Only `twitch` for now; others are added over time."},{"name":"query","in":"query","required":true,"schema":{"type":"string","maxLength":100}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}}],"responses":{"200":{"description":"Matching categories","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StreamCategory"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"description":"The token lacks the scope (`insufficient_scope`), or the platform connection lacks a scope it needs and has to be renewed (`platform_reconnect_required`, with `platform`, `missing_scopes` and `reconnect_url`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"404":{"description":"Unknown platform.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The platform is not connected to this creator (`platform_not_connected`, with `reconnect_url`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid input (`validation_failed`), or the platform refused (`platform_rejected`, with `platform_status` and `platform_message`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The platform could not be reached (`platform_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-gleem-scope":"stream:read","description":"Part of the Gleem IRL beta: without Gleem IRL on the account the answer is `403` with the code `feature_unavailable`."}},"/stream/{platform}/chatters":{"get":{"operationId":"listStreamChatters","summary":"List chatters","tags":["Stream Manager"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["twitch"]},"description":"The streaming platform. Only `twitch` for now; others are added over time."},{"name":"cursor","in":"query","schema":{"type":"string","maxLength":512},"description":"The `cursor` of the previous page, to read the next one."},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":1000,"default":1000}}],"responses":{"200":{"description":"One page of the users in chat right now","content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/StreamChatter"}},"total":{"type":"integer","description":"Everyone in chat, across all pages, as the platform counts them."},"cursor":{"type":["string","null"],"description":"Pass as `cursor` for the next page; `null` on the last one."}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"description":"The token lacks the scope (`insufficient_scope`), or the platform connection lacks `moderator:read:chatters` and has to be renewed (`platform_reconnect_required`, with `platform`, `missing_scopes` and `reconnect_url`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown platform.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The platform is not connected to this creator (`platform_not_connected`, with `reconnect_url`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid input (`validation_failed`), the platform has no chatter list (`platform_unsupported`), or the platform refused (`platform_rejected`, with `platform_status` and `platform_message`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"description":"The platform could not be reached (`platform_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-gleem-scope":"stream:read","description":"Who is in the creator's chat right now, a page at a time. Each page is cached for about 20 seconds, so polling faster gains nothing. There is no search; filter the pages you have. Part of the Gleem IRL beta: without Gleem IRL on the account the answer is `403` with the code `feature_unavailable`."}},"/stream/chat":{"get":{"operationId":"listStreamChat","summary":"Recent chat messages","tags":["Stream Manager"],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"before","in":"query","schema":{"type":"string"},"description":"An envelope `id`; returns what came before it."},{"name":"platform","in":"query","schema":{"type":"string"},"description":"Keep one platform, such as `twitch` or `gleem`."}],"description":"`chat.message` envelopes from the last day, newest first, deleted ones flagged. Part of the Gleem IRL beta: without Gleem IRL on the account the answer is `403` with the code `feature_unavailable`.","responses":{"200":{"description":"Messages","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StreamEvent"}},"has_more":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"stream:read"}},"/stream/activity":{"get":{"operationId":"listStreamActivity","summary":"Recent activity","tags":["Stream Manager"],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"before","in":"query","schema":{"type":"string"},"description":"An envelope `id`; returns what came before it."},{"name":"platform","in":"query","schema":{"type":"string"},"description":"Keep one platform, such as `twitch` or `gleem`."}],"description":"`activity.*` envelopes (follows, subs, gifts, raids, redemptions, Gems) from the last day, newest first. Part of the Gleem IRL beta: without Gleem IRL on the account the answer is `403` with the code `feature_unavailable`.","responses":{"200":{"description":"Activity","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StreamEvent"}},"has_more":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"stream:read"}},"/stream/{platform}/chat/messages":{"post":{"operationId":"sendStreamChatMessage","summary":"Send a chat message as the streamer","tags":["Stream Manager"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["twitch"]},"description":"The streaming platform. Only `twitch` for now; others are added over time."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text"],"properties":{"text":{"type":"string","maxLength":500},"reply_to_message_id":{"type":["string","null"]}}}}}},"responses":{"201":{"description":"Sent","content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string"},"message_id":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"description":"The token lacks the scope (`insufficient_scope`), or the platform connection lacks a scope it needs and has to be renewed (`platform_reconnect_required`, with `platform`, `missing_scopes` and `reconnect_url`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"404":{"description":"Unknown platform.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The platform is not connected to this creator (`platform_not_connected`, with `reconnect_url`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid input (`validation_failed`), the platform refused (`platform_rejected`), or it dropped the message (`message_dropped`, with `drop_reason`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The platform could not be reached (`platform_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-gleem-scope":"stream:write","description":"Part of the Gleem IRL beta: without Gleem IRL on the account the answer is `403` with the code `feature_unavailable`."}},"/stream/{platform}/chat/messages/{messageId}":{"delete":{"operationId":"deleteStreamChatMessage","summary":"Delete a chat message","tags":["Stream Manager"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["twitch"]},"description":"The streaming platform. Only `twitch` for now; others are added over time."},{"name":"messageId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"description":"The token lacks the scope (`insufficient_scope`), or the platform connection lacks a scope it needs and has to be renewed (`platform_reconnect_required`, with `platform`, `missing_scopes` and `reconnect_url`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"404":{"description":"Unknown platform.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The platform is not connected to this creator (`platform_not_connected`, with `reconnect_url`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid input (`validation_failed`), or the platform refused (`platform_rejected`, with `platform_status` and `platform_message`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The platform could not be reached (`platform_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-gleem-scope":"stream:write","description":"Part of the Gleem IRL beta: without Gleem IRL on the account the answer is `403` with the code `feature_unavailable`."}},"/stream/{platform}/bans":{"post":{"operationId":"banStreamUser","summary":"Ban or time out a user","tags":["Stream Manager"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["twitch"]},"description":"The streaming platform. Only `twitch` for now; others are added over time."}],"description":"As the streamer. With `duration_seconds` (up to two weeks) it is a timeout. Part of the Gleem IRL beta: without Gleem IRL on the account the answer is `403` with the code `feature_unavailable`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["user_id"],"properties":{"user_id":{"type":"string"},"duration_seconds":{"type":["integer","null"],"minimum":1,"maximum":1209600},"reason":{"type":["string","null"],"maxLength":500}}}}}},"responses":{"200":{"description":"Banned","content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string"},"user_id":{"type":"string"},"expires_at":{"type":["string","null"],"format":"date-time"}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"description":"The token lacks the scope (`insufficient_scope`), or the platform connection lacks a scope it needs and has to be renewed (`platform_reconnect_required`, with `platform`, `missing_scopes` and `reconnect_url`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"404":{"description":"Unknown platform.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The platform is not connected to this creator (`platform_not_connected`, with `reconnect_url`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid input (`validation_failed`), or the platform refused (`platform_rejected`, with `platform_status` and `platform_message`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The platform could not be reached (`platform_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-gleem-scope":"stream:write"}},"/stream/{platform}/bans/{userId}":{"delete":{"operationId":"unbanStreamUser","summary":"Lift a ban or timeout","tags":["Stream Manager"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["twitch"]},"description":"The streaming platform. Only `twitch` for now; others are added over time."},{"name":"userId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Lifted"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"description":"The token lacks the scope (`insufficient_scope`), or the platform connection lacks a scope it needs and has to be renewed (`platform_reconnect_required`, with `platform`, `missing_scopes` and `reconnect_url`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"404":{"description":"Unknown platform.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The platform is not connected to this creator (`platform_not_connected`, with `reconnect_url`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid input (`validation_failed`), or the platform refused (`platform_rejected`, with `platform_status` and `platform_message`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The platform could not be reached (`platform_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-gleem-scope":"stream:write","description":"Part of the Gleem IRL beta: without Gleem IRL on the account the answer is `403` with the code `feature_unavailable`."}},"/stream/{platform}/redemptions/{redemptionId}":{"patch":{"operationId":"updateStreamRedemption","summary":"Fulfil or refund a channel point redemption","tags":["Stream Manager"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["twitch"]},"description":"The streaming platform. Only `twitch` for now; others are added over time."},{"name":"redemptionId","in":"path","required":true,"schema":{"type":"string"},"description":"The `redemption_id` of an `activity.redemption`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["fulfilled","canceled"],"description":"`canceled` refunds the viewer's points. Upper case is accepted too."},"reward_id":{"type":"string","maxLength":64,"description":"The reward's id. Only needed for a redemption no longer in the activity backlog; otherwise it is taken from there."}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string"},"redemption_id":{"type":"string"},"reward_id":{"type":"string"},"status":{"type":"string","enum":["fulfilled","canceled"]}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"description":"The token lacks the scope (`insufficient_scope`), the platform connection lacks `channel:manage:redemptions` (`platform_reconnect_required`, with `platform`, `missing_scopes` and `reconnect_url`), or the reward was not created by Gleem (`reward_not_manageable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown platform, or the redemption is not known here and no `reward_id` was given, or the platform does not know it or it is no longer unfulfilled (`redemption_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The platform is not connected (`platform_not_connected`, with `reconnect_url`), or the redemption is already fulfilled or refunded (`redemption_already_processed`, with `status`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid input (`validation_failed`), or the platform refused (`platform_rejected`, with `platform_status` and `platform_message`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"description":"The platform could not be reached (`platform_unavailable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"x-gleem-scope":"stream:write","description":"Only redemptions of rewards Gleem created can be updated: `can_manage` on the `activity.redemption` says which. The card takes the new status and the Stream Manager channel announces `activity.redemption.updated`. Part of the Gleem IRL beta: without Gleem IRL on the account the answer is `403` with the code `feature_unavailable`."}},"/stream/quick-actions":{"get":{"operationId":"showQuickActions","summary":"The Quick Actions layout","tags":["Stream Manager"],"responses":{"200":{"description":"The layout, or an empty one at revision 0","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuickActionLayout"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"stream:read","description":"Part of the Gleem IRL beta: without Gleem IRL on the account the answer is `403` with the code `feature_unavailable`."},"put":{"operationId":"updateQuickActions","summary":"Save the Quick Actions layout","tags":["Stream Manager"],"description":"Replaces the layout. With `revision`, the save only goes through when nobody saved since; otherwise `409` `revision_conflict` with the current `layout`. Part of the Gleem IRL beta: without Gleem IRL on the account the answer is `403` with the code `feature_unavailable`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tiles"],"properties":{"tiles":{"type":"array","maxItems":48,"items":{"$ref":"#/components/schemas/QuickActionTile"}},"revision":{"type":["integer","null"]}}}}}},"responses":{"200":{"description":"The saved layout","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuickActionLayout"}}}},"409":{"description":"Saved elsewhere since `revision` (`revision_conflict`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid tiles.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"stream:write"}},"/webhooks":{"get":{"operationId":"listWebhooks","summary":"The app's webhooks for this creator","tags":["Webhooks"],"responses":{"200":{"description":"The app's webhooks. Never the creator's own, and never another app's.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AppWebhook"}}}}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"webhooks:write","description":"Requires the `webhooks:write` scope. OAuth access tokens only."},"post":{"operationId":"createWebhook","summary":"Register a webhook","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"An https URL on a public address."},"event_types":{"type":["array","null"],"items":{"type":"string"},"description":"The event types to send. Each needs the scope that covers it (see the events reference). Omit or null for every type the app's scopes cover."}}}}}},"responses":{"201":{"description":"The webhook, with its signing secret. The secret is only ever returned here.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/AppWebhook"},{"type":"object","properties":{"secret":{"type":"string","description":"`whsec_…`, for checking the `Gleem-Signature` header."}}}]}}}},"409":{"description":"`limit_reached`: the app already has 5 webhooks for this creator.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"A bad URL, or an event type the app's scopes do not cover.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"webhooks:write","description":"Requires the `webhooks:write` scope. OAuth access tokens only. Deliveries use the same envelope, signature and retries as dashboard webhooks."}},"/webhooks/{webhook}":{"delete":{"operationId":"deleteWebhook","summary":"Delete a webhook","tags":["Webhooks"],"parameters":[{"name":"webhook","in":"path","required":true,"schema":{"type":"integer"},"description":"The webhook id."}],"responses":{"204":{"description":"Deleted."},"404":{"description":"No such webhook of this app.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}},"x-gleem-scope":"webhooks:write","description":"Requires the `webhooks:write` scope. OAuth access tokens only."}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A personal access token, `gleem_pat_…`, from the dashboard's Developer page, or an OAuth access token, `gleem_oat_…`. Treat both as opaque."},"oauth2":{"type":"oauth2","description":"For third-party apps: authorization code with PKCE (S256), the device authorization grant, or client credentials for app-wide endpoints. The token goes in the same `Authorization: Bearer` header. See https://gleem.gg/developers/oauth.","flows":{"authorizationCode":{"authorizationUrl":"https://gleem.gg/oauth/authorize","tokenUrl":"https://gleem.gg/oauth/token","refreshUrl":"https://gleem.gg/oauth/token","scopes":{"events:read":"Receive live events from the channel","tips:read":"Read Gem messages and the leaderboard","queue:read":"See the Treat queue","queue:write":"Open and close the queue, move entries along and refund them","treats:read":"See Treats","treats:write":"Show, hide and pin Treats","tickets:write":"Grant Trigger Tickets","members:read":"See membership tiers and member counts","irl:read":"See IRL Sidekicks and how their streams are doing","irl:network":"Sensitive: see the IP addresses of IRL Sidekicks' network connections, on top of irl:read","irl:write":"Control IRL Sidekicks and add new ones, such as the phone the Gleem IRL app runs on","hosting:read":"See OBS rentals, including how to connect to their OBS","hosting:write":"Start and end OBS rentals (reserved; no endpoint uses it yet)","webhooks:write":"Register webhooks for the creator (OAuth apps only)"}},"clientCredentials":{"tokenUrl":"https://gleem.gg/oauth/token","scopes":[]}}}},"responses":{"Unauthenticated":{"description":"Missing, unknown, expired or revoked token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The token lacks the scope (`insufficient_scope`), or the resource is another creator's (`forbidden`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Over 120 requests a minute. See Retry-After.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable, machine-readable.","examples":["insufficient_scope"]},"message":{"type":"string","description":"For people; may change."},"required_scope":{"type":"string"},"fields":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}}}},"Member":{"type":"object","required":["id","display_name","login","twitch_id"],"properties":{"id":{"type":"integer"},"display_name":{"type":"string"},"login":{"type":["string","null"],"description":"The Twitch login (lowercase handle). Null until Gleem has learned it; fall back to display_name."},"twitch_id":{"type":["string","null"]}}},"TreatSummary":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"icon":{"type":"string"}}},"QueueSession":{"type":"object","properties":{"id":{"type":"integer"},"status":{"type":"string","enum":["open","closed"]},"total_slots":{"type":"integer"},"closes_at":{"type":["string","null"],"description":"HH:MM in `timezone`."},"timezone":{"type":"string"},"waiting":{"type":"integer"},"opened_at":{"type":["string","null"],"format":"date-time"}}},"QueueEntry":{"type":"object","properties":{"id":{"type":"integer"},"session_id":{"type":"integer"},"position":{"type":"integer"},"status":{"type":"string","enum":["pending","active","completed","cancelled"]},"refunded":{"type":"boolean"},"requested_at":{"type":["string","null"],"format":"date-time"},"redemption_id":{"type":["string","null"]},"member":{"$ref":"#/components/schemas/Member"},"treat":{"oneOf":[{"$ref":"#/components/schemas/TreatSummary"},{"type":"null"}]},"payment_type":{"type":"string","enum":["gems","ticket"]},"gems":{"type":"integer"},"input_label":{"type":["string","null"]},"input":{"type":["string","null"]},"input_filtered":{"type":"boolean","description":"True when the creator's message filter masked or removed the input."}}},"Treat":{"type":"object","properties":{"id":{"type":"integer"},"category_id":{"type":"integer"},"name":{"type":"string"},"description":{"type":["string","null"]},"icon":{"type":"string"},"price_gems":{"type":"integer"},"price_tickets":{"type":"integer"},"is_active":{"type":"boolean"},"is_pinned":{"type":"boolean"},"available":{"type":"boolean"},"is_visible":{"type":"boolean","description":"Active and in an active category."},"input":{"oneOf":[{"type":"object","properties":{"label":{"type":["string","null"]},"required":{"type":"boolean"},"max_length":{"type":"integer"}}},{"type":"null"}]}}},"TreatCategory":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"icon":{"type":"string"},"is_active":{"type":"boolean"},"treats":{"type":"array","items":{"$ref":"#/components/schemas/Treat"}}}},"Tip":{"type":"object","properties":{"id":{"type":"integer"},"member":{"$ref":"#/components/schemas/Member"},"gems":{"type":"integer"},"message":{"type":["string","null"]},"gif":{"oneOf":[{"type":"object","properties":{"url":{"type":"string"},"width":{"type":"integer"},"height":{"type":"integer"},"title":{"type":"string"}}},{"type":"null"}]},"created_at":{"type":"string","format":"date-time"},"message_filtered":{"type":"boolean","description":"True when the creator's message filter masked or removed the message."}}},"IrlLink":{"type":"object","properties":{"link_id":{"type":["integer","null"]},"iface":{"type":["string","null"],"description":"The device's network interface, e.g. `wwan0`."},"state":{"type":["string","null"],"description":"`down`, `registering`, `live` or `dead`."},"rtt_ms":{"type":["number","null"],"description":"Round trip to ingest over this link. Null until the link has answered once."},"loss_pct":{"type":["number","null"],"description":"Packets lost over the last second, 0 to 100."},"tx_bps":{"type":["integer","null"],"description":"Bits per second sent over this link."}}},"IrlDevice":{"type":"object","properties":{"uuid":{"type":"string","format":"uuid"},"name":{"type":"string"},"board":{"type":["string","null"],"description":"What the device runs on: a board such as `rk3588` for an IRL Sidekick, or `android` / `ios` for a phone running the Gleem IRL app."},"hardware":{"type":["string","null"],"description":"The device's model, as it reports it."},"version":{"type":["string","null"],"description":"The IRL Sidekick version the device runs, or the app's version for a phone running the Gleem IRL app.","examples":["0.0.3"]},"temperature_c":{"type":["number","null"],"description":"The hottest temperature sensor the device last reported, in °C. Null while it is offline, and for a device that reports none, such as a phone.","examples":[61.5]},"online":{"type":"boolean","description":"The device reported within the last 15 seconds."},"last_seen_at":{"type":["string","null"],"format":"date-time"},"stream":{"type":["object","null"],"description":"The device's own view of its stream. Null while the device is offline or idle.","properties":{"state":{"type":"string","enum":["starting","live","stopping","error"]},"sid":{"type":["string","null"],"description":"The ingest session, new for every stream start."},"started_at":{"type":["string","null"],"format":"date-time"},"error":{"type":["string","null"],"description":"The device's last error message."},"healthy":{"type":"boolean","description":"Live on the device, the encoder connected, at least one live link, and ingest passing the stream to viewers and reporting it within the last 20 seconds. Switch to a fallback scene when this turns false."},"encoder_connected":{"type":"boolean","description":"The device's encoder is sending video."},"bitrate_bps":{"type":["integer","null"],"description":"What the device sends over all links, in bits per second."},"rtt_ms":{"type":["number","null"],"description":"Round trip the bonded stream sees: the mean over the live links, weighted by how much each one carries. Null without a live link."},"links":{"type":"array","items":{"$ref":"#/components/schemas/IrlLink"}}}},"ingest":{"type":["object","null"],"description":"What the ingest server last reported for the open session. Null without one.","properties":{"region":{"type":"string"},"publishing":{"type":"boolean","description":"Ingest is passing the stream on to viewers."},"last_reported_at":{"type":["string","null"],"format":"date-time"},"current_bps":{"type":"integer","description":"What ingest measured in its latest report (every 10 s), the same rate the dashboard shows as arriving at ingest."},"average_bps":{"type":"integer","description":"The mean over the whole session."},"peak_bps":{"type":"integer"}}},"phone":{"type":["object","null"],"description":"What a phone running the Gleem IRL app last said about itself. Null for an IRL Sidekick, and while the device is offline.","properties":{"battery_pct":{"type":["integer","null"],"minimum":0,"maximum":100},"charging":{"type":["boolean","null"]},"thermal":{"type":["string","null"],"enum":["nominal","fair","serious","critical",null]},"camera":{"type":["string","null"],"description":"The camera in use, as the app names it."},"preset":{"type":["string","null"],"description":"The streaming preset in use, as the app names it."}}},"interfaces":{"type":["array","null"],"description":"The device's network interfaces as it last reported them. Null while the device is offline. `addresses` is null unless the token also holds `irl:network`.","items":{"type":"object","properties":{"name":{"type":["string","null"],"examples":["wwan0"]},"kind":{"type":["string","null"],"enum":["modem","wifi","ethernet","usb_tether","other",null]},"state":{"type":["string","null"],"enum":["up","down",null]},"addresses":{"type":["array","null"],"description":"The interface's IPv4 and IPv6 addresses. Only with the sensitive `irl:network` scope, never for a token issued for a rented OBS machine; null otherwise.","items":{"type":"string"}}}}},"bitrate":{"type":["object","null"],"description":"The bitrate window the device is set to stream with, as the dashboard shows it: what was set on gleem.gg, else what the device reports. Null while neither has any, such as for a phone. Move it with `PUT /irl/devices/{uuid}/bitrate`.","properties":{"min_kbps":{"type":"integer"},"max_kbps":{"type":"integer"}}},"poll_interval_seconds":{"type":"integer","description":"How often to poll at most. The device reports every 5 seconds and ingest every 10, so faster polling sees nothing new."},"telemetry_socket":{"type":"object","description":"Where every heartbeat of the device is pushed as it arrives, instead of polling. Sign `channel` at `auth_url` with a token holding `irl:read`: it carries no network addresses. `network_channel` carries the same events with them and needs `irl:network` too. See https://gleem.gg/developers/websocket#network-addresses.","properties":{"url":{"type":"string"},"channel":{"type":"string","examples":["private-irl-device.9d3c2f4e-6b1a-4c8e-9f2d-1a2b3c4d5e6f.telemetry"]},"network_channel":{"type":"string","examples":["private-irl-device.9d3c2f4e-6b1a-4c8e-9f2d-1a2b3c4d5e6f.network"]},"auth_url":{"type":"string"}}}}},"AppWebhook":{"type":"object","properties":{"id":{"type":"integer"},"url":{"type":"string","format":"uri"},"event_types":{"type":["array","null"],"items":{"type":"string"},"description":"Null for every event type the app's scopes cover, including new ones."},"is_active":{"type":"boolean","description":"False once Gleem switched it off after 15 failed deliveries in a row, or the creator switched it off."},"disabled_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"}}},"IrlCommand":{"type":"object","description":"A command sent to an online device. It expires unrun after 60 seconds.","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["stream.start","stream.stop","stream.bitrate","restart","update.install"]},"status":{"type":"string","enum":["pending","sent","done","failed","expired"]},"payload":{"type":"object","description":"`{\"target\": …}` for a restart, the window for `stream.bitrate`, else empty."},"result":{"type":["object","null"]},"error":{"type":["object","null"]},"issued_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"}}},"IrlRealtime":{"type":"object","description":"Where the device itself receives its commands, over the Pusher protocol. It signs its channel at `auth_url` with its `gid_` credential.","properties":{"url":{"type":"string"},"channel":{"type":"string","examples":["private-irl-device.9d3c2f4e-6b1a-4c8e-9f2d-1a2b3c4d5e6f"]},"auth_url":{"type":"string"}}},"IrlPairing":{"type":"object","description":"An IRL Sidekick waiting to be paired.","required":["code","hardware","version","expires_at"],"properties":{"code":{"type":"string","description":"The code in its full form.","examples":["GLEEM-7K3M-Q9TX"]},"hardware":{"type":["string","null"],"description":"What the device said it is; `null` if it did not say.","examples":["Orange Pi 5 Plus"]},"version":{"type":["string","null"],"description":"The IRL Sidekick version the device runs.","examples":["0.0.3"]},"expires_at":{"type":"string","format":"date-time","description":"When the code stops working."}}},"Rental":{"type":"object","properties":{"uuid":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["requested","provisioning","running","stopping","ended","failed"]},"active":{"type":"boolean","description":"The rental still occupies the machine."},"price_per_hour":{"type":"string","description":"The agreed price per hour, as a decimal with four places.","examples":["0.0577"]},"price_cents":{"type":"integer","description":"The agreed price per hour, rounded to whole cents. Prefer price_per_hour."},"currency":{"type":"string","examples":["EUR"]},"accrued":{"type":"string","description":"What the rental has cost so far, as metered, as a decimal with four places.","examples":["0.0433"]},"accrued_cents":{"type":"integer","description":"What the rental has cost so far, rounded to whole cents. Prefer accrued."},"max_hours":{"type":"integer"},"width":{"type":"integer"},"height":{"type":"integer"},"framerate":{"type":"integer"},"requested_at":{"type":"string","format":"date-time"},"started_at":{"type":["string","null"],"format":"date-time"},"ended_at":{"type":["string","null"],"format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time","description":"When the rental hits its hard cap, counted from `started_at`."},"ended_reason":{"type":["string","null"],"enum":["renter_stopped","host_stopped","admin_stopped","max_hours_reached","machine_lost","agent_failed",null]},"machine":{"type":["object","null"],"properties":{"uuid":{"type":"string","format":"uuid"},"name":{"type":"string"},"gpu":{"type":["string","null"]},"region":{"type":["string","null"]},"city":{"type":["string","null"]}}},"obs_websocket":{"type":["object","null"],"description":"How to reach the rental's OBS over obs-websocket 5, through Gleem's relay. Only while the rental is not finished; null afterwards. The password controls the OBS: keep it as safe as the token.","properties":{"url":{"type":"string","examples":["wss://obs.gleem.gg/2b7e0c1a-4f3d-4a8e-9c6b-5d1e2f3a4b5c"]},"host":{"type":"string"},"port":{"type":["integer","null"]},"password":{"type":"string"}}}}},"StreamUser":{"type":"object","description":"A user on a streaming platform, with that platform's ids.","properties":{"id":{"type":"string"},"login":{"type":["string","null"]},"display_name":{"type":["string","null"]}}},"StreamCategory":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"box_art_url":{"type":["string","null"],"format":"uri"}}},"StreamChatter":{"type":"object","properties":{"user_id":{"type":"string"},"login":{"type":"string"},"display_name":{"type":"string"}}},"StreamPlatformStatus":{"type":"object","properties":{"platform":{"type":"string","examples":["twitch"]},"connected":{"type":"boolean"},"channel":{"oneOf":[{"$ref":"#/components/schemas/StreamUser"},{"type":"null"}]},"live":{"type":"boolean"},"viewers":{"type":["integer","null"]},"started_at":{"type":["string","null"],"format":"date-time"},"title":{"type":["string","null"]},"category":{"oneOf":[{"$ref":"#/components/schemas/StreamCategory"},{"type":"null"}]},"capabilities":{"type":"object","description":"What the connection allows. `chat_read` also needs Gleem's bot to be running.","properties":{"chat_read":{"type":"boolean"},"chat_send":{"type":"boolean"},"chat_delete":{"type":"boolean"},"ban":{"type":"boolean"},"edit_info":{"type":"boolean"},"follows":{"type":"boolean"},"redemptions":{"type":"boolean"},"chatters":{"type":"boolean"}}},"missing_scopes":{"type":"array","items":{"type":"string"},"description":"Platform scopes the connection lacks. Twitch sign-in never grants the Stream Manager's scopes; the creator grants them once at `reconnect_url`."},"reconnect_url":{"type":["string","null"],"format":"uri","description":"Where to send the creator, in a browser, to connect the platform or renew it with the Stream Manager's permissions; null while nothing is missing. It signs them in on gleem.gg first if needed. For Twitch it only accepts the Twitch account already linked to the creator."},"error":{"type":["string","null"],"description":"`platform_unavailable` when the platform could not be asked; the live fields are then empty."}}},"StreamEvent":{"type":"object","description":"The Stream Manager envelope, the same in the backlog and on the WebSocket.","properties":{"id":{"type":"string","description":"A ULID."},"type":{"type":"string","examples":["chat.message","activity.follow"]},"platform":{"type":"string","examples":["twitch","gleem"]},"occurred_at":{"type":"string","format":"date-time"},"deleted":{"type":"boolean","description":"A chat message a moderator removed."},"data":{"type":"object","description":"Depends on `type`; see the Stream Manager docs."}}},"QuickActionTile":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","maxLength":64,"pattern":"^[A-Za-z0-9._:-]+$"},"type":{"type":"string","maxLength":64,"pattern":"^[A-Za-z0-9._:-]+$"},"label":{"type":["string","null"],"maxLength":40},"icon":{"type":["string","null"],"maxLength":64},"config":{"type":["object","null"]}}},"QuickActionLayout":{"type":"object","properties":{"tiles":{"type":"array","items":{"$ref":"#/components/schemas/QuickActionTile"}},"revision":{"type":"integer"},"updated_at":{"type":["string","null"],"format":"date-time"}}}}}}